505 Commits

Author SHA1 Message Date
Hermes Agent service account
d974c75d7c feat(jmri): headless JMRI server with Leviton layout power monitor and X11 GUI mode
- Stable udev device symlinks (/dev/jmri/nce, /dev/jmri/loconet, /dev/jmri/lcc)
- jmri-monitor: polls Leviton Decora Smart switch to start/stop JMRI automatically
  - Quiet hours 1-10 AM (no polling)
  - 30s off-delay before shutdown
- LCRR config cloned from Gitea (ssh://gitea.mk-labs.cloud:2221/rblundon/LCRR.git)
- ~/.jmri symlinked to LCRR repo for GitOps config management
- jmri-gui: X11 remote GUI access (PanelPro/DecoderPro) via ssh -X as jmri user
  - Stops daemon, launches GUI, restarts daemon on exit if layout still on
- jmri user gets login shell + SSH key for GUI sessions
- Full JRE installed (openjdk-21-jre) for AWT/X11 support
2026-07-29 00:43:23 -05:00
Hermes Agent service account
a5433dcb5b minecraft: queue AntiSilverFish v0.0.4 — apply on next restart 2026-07-20 00:39:46 -05:00
Hermes Agent service account
e0eb47f5ce minecraft: add sleep-most to PLUGINS url list so it survives pod restarts 2026-07-19 21:18:57 -05:00
Hermes Agent service account
a8822f0778 minecraft: disable whitelist — open server 2026-07-19 21:02:32 -05:00
bedf87b492 change seed 2026-07-19 20:56:03 -05:00
Hermes Agent service account
b6f7791c98 minecraft: add SkinsRestorer v15.12.4 plugin for offline-mode skin support 2026-07-19 20:48:22 -05:00
Hermes Agent service account
1a48e60afd minecraft: fix Grafana dashboard queries for prometheus-exporter v3 mc_ metric names 2026-07-19 18:27:22 -05:00
Hermes Agent service account
c26b19793b minecraft: bind prometheus exporter to 0.0.0.0 — localhost blocks Prometheus scrape 2026-07-19 18:20:11 -05:00
Hermes Agent service account
dfe81a5c20 minecraft: set prometheus exporter to port 9225, manage config via ConfigMap 2026-07-19 18:17:47 -05:00
Hermes Agent service account
4afb05e56b minecraft: wire PLUGINS env var into Deployment — prometheus exporter was never downloaded 2026-07-19 18:15:37 -05:00
Hermes Agent service account
46b49259d2 minecraft: manage sleep-most config via ConfigMap — single player sleep enabled 2026-07-19 18:13:55 -05:00
Hermes Agent service account
3f3ce68e18 minecraft: fix backup script — skip missing nether/end dirs, safe save-on on tar failure 2026-07-19 17:52:54 -05:00
Hermes Agent service account
e44805c9b6 minecraft: fix backup image — itzg/rcon-cli is distroless, use minecraft-server instead 2026-07-19 17:51:15 -05:00
Hermes Agent service account
1aa6b4234a minecraft: add hourly world backup CronJob with 3-day local retention 2026-07-19 17:48:53 -05:00
Hermes Agent service account
4cde540e70 minecraft: enable whitelist with RyansRailroad, Nylarac19, ga_eul_pabo, Ghoulish_Hannah 2026-07-19 17:39:30 -05:00
Hermes Agent service account
69fb5f5641 minecraft: disable online-mode to bypass Zscaler session auth blocking 2026-07-19 17:35:22 -05:00
Hermes Agent service account
430552a0b1 fix(minecraft): set enforce-secure-profile=false to bypass Mojang WAF block on homelab egress IP 2026-07-19 15:01:30 -05:00
Hermes Agent service account
18bb111843 feat(minecraft): add Prometheus metrics + Grafana dashboard
- minecraft-prometheus-exporter v3.1.2 plugin (port 9225)
- ServiceMonitor for Prometheus scraping
- Grafana dashboard ConfigMap (ID 20659, Minecraft server stats)
- metrics port added to Service and Deployment containerPorts
2026-07-19 14:53:36 -05:00
Hermes Agent service account
712425ee17 feat(minecraft): upgrade PaperMC to 26.2 (build 62) 2026-07-19 14:22:06 -05:00
Hermes Agent service account
1d77821e5f feat(minecraft): add Cloudflare ExternalDNS annotations for public DNS record 2026-07-19 14:17:14 -05:00
Hermes Agent service account
7ad40bb509 fix(minecraft): remove SleepMost plugin 2026-07-19 13:52:37 -05:00
Hermes Agent service account
3950a2b069 fix(minecraft): correct SleepMost plugin URL to v5.5.3 (5.6.2 never existed) 2026-07-19 13:50:15 -05:00
Hermes Agent service account
d20fd80798 fix(minecraft): use px-fa-direct-access storage class for world data PVC 2026-07-19 13:47:12 -05:00
Hermes Agent service account
308ee553c3 fix(minecraft): exclude application.yaml from self-sync to resolve SharedResourceWarning 2026-07-19 13:44:30 -05:00
Hermes Agent service account
56110d52bd feat(minecraft): deploy journey-into-imagination PaperMC server
- PaperMC 26.1.2 via itzg/minecraft-server:2026.7.0
- Namespace: minecraft, Service: journey-into-imagination
- TCP port 10182 (non-standard) via ingress-nginx tcp forwarding
- Pure Storage CSI PVC (pure-block, 50Gi) for world data
- World seed hardcoded: -5177989977648707969
- RCON password via ExternalSecret + 1Password Connect
- SleepMost v5.6.2 plugin for single-player sleep
- Whitelist off at launch, toggle-ready
- ExternalDNS annotations for internal Technitium record
- Manual steps: UniFi port forward WAN:10182→10.1.71.80:10182,
  Cloudflare A record + SRV for journey-into-imagination.mk-labs.cloud
2026-07-19 13:36:27 -05:00
Hermes Agent service account
1f07fdff45 revert: restore wed as ansible_user for main-street-station 2026-07-18 20:03:20 -05:00
Hermes Agent service account
317816558d fix: main-street-station uses jarvis user and id_jarvis key 2026-07-18 19:58:02 -05:00
Hermes Agent service account
ea22e4e407 fix: update main-street-station IP to 192.168.10.40 2026-07-18 19:38:46 -05:00
Hermes Agent service account
490c483924 feat: add JMRI headless server role and main-street-station host
- New ansible/roles/jmri role: installs OpenJDK 21 headless, creates
  jmri service user, downloads JMRI 5.10, deploys JmriFaceless systemd unit
- Handles dialout group membership for serial device access
- Config restore task for post-reinstall recovery from GitHub backup
- host_vars/main-street-station: profile_id and serial device (TODO: fill in)
- Inventory: jmri_server group with main-street-station at 192.168.10.45
- Playbook: day1_deploy_jmri.yml (linux-baseline + jmri)
2026-07-18 19:35:01 -05:00
Hermes Agent service account
bc34a1f915 couchdb: increase nginx proxy body size to 100m
Fixes 413 Entity Too Large error in Obsidian LiveSync sync operations.
Applies to both internal (communicore.local) and public (communicore.mk-labs.cloud) ingress routes.
2026-06-30 23:09:56 -05:00
Hermes Agent service account
64e690737e fix(traefik): remove WebSocket middleware - Traefik v3 handles WS natively 2026-06-30 17:19:29 -05:00
Hermes Agent service account
0693fdcd26 fix(traefik): add WebSocket middleware for Hermes dashboard Chat tab
- Add websocket-headers middleware to jarvis router
- Set Connection: Upgrade and Upgrade: websocket headers
- Fixes 'Chat unavailable:1' WebSocket connection failures through Traefik reverse proxy
2026-06-30 17:12:59 -05:00
19a807899f reference file 2026-06-29 20:55:25 -05:00
5bacf9fbca talos multipath patch 2026-06-29 20:49:57 -05:00
1da9bfd43c talos 2026-06-25 10:02:16 -05:00
0bc9b2e788 Continued talos multipath troubleshooting. 2026-06-22 22:30:35 -05:00
dcfb6825e8 Talos multipath. 2026-06-22 20:40:06 -05:00
0b9ac4dc74 Pre-upgrade snapshot: Talos v1.13.2, before multipath implementation 2026-06-22 18:39:35 -05:00
Hermes Agent service account
aabf758c91 Add multipath.conf for Pure FlashArray to Talos worker nodes
- Add /etc/multipath.conf file creation in worker patches
- Configuration optimized for Pure FlashArray iSCSI
- Required for PX-CSI node pods to start successfully
- Blacklists Portworx virtual devices (pxd*)

Ref: Portworx → democratic-csi migration Phase 3
2026-06-22 16:53:38 -05:00
Hermes Agent service account
489b8aeb35 WIP: iscsi-multipath-init DaemonSet attempts
Successfully writes /system/etc/multipath.conf but cannot write to /etc
due to Talos read-only filesystem restrictions.

Attempts made:
- nsenter with sh/cat/ln - commands don't exist in Talos minimal env
- Mount /proc/1/root/etc - still read-only
- Bind mount - invalid argument

Blocker: Talos /etc is truly read-only post-boot. PX-CSI also fails
with same nsenter/command issues when trying to validate multipath.conf.

Next: Investigate PX-CSI configuration options or Talos machine config alternatives.
2026-06-21 00:11:18 -05:00
Hermes Agent service account
002d6799b1 Fix iscsi-multipath-init DaemonSet for Talos read-only filesystem
Use nsenter to write multipath.conf in host's mount namespace instead
of trying to write to /host/etc which is read-only in containers.

Talos mounts /etc as read-only in container namespaces but allows writes
in the host mount namespace. This fix uses nsenter to access PID 1's
mount namespace where /etc is writable.

Also removed unnecessary volumeMounts and volumes since we're using
nsenter instead of hostPath mounts.

Fixes: Init:Error - 'can't create /host/etc/multipath.conf: Read-only file system'
2026-06-20 23:56:20 -05:00
Hermes Agent service account
150cef1aca Add task completion summary for jungle-cruise recovery
Comprehensive summary of diagnosis, fix, and recovery status.
Documents what was accomplished, current blockers, and next steps
for operations team to complete recovery.
2026-06-20 22:13:00 -05:00
Hermes Agent service account
a30ad99ee4 Add jungle-cruise recovery documentation
Documents root cause analysis and recovery procedure for jungle-cruise
node failure after applying multipath.conf via machine.files.

Includes three recovery options depending on available credentials:
- Apply fixed config (requires talosctl + existing configs)
- Force reboot (quickest)
- Full regeneration (requires SOPS keys)
2026-06-20 22:11:44 -05:00
Hermes Agent service account
d2b6d95a49 Revert multipath.conf from machine.files
Removes /etc/multipath.conf from machine.files section which causes
jungle-cruise boot failure. This reverts the problematic change from
commit adc415e.

Root cause: Writing /etc/multipath.conf during early boot via machine.files
causes writeUserFiles to fail on read-only filesystem.

Solution: Use DaemonSet (iscsi-multipath-init.yaml) to write multipath.conf
after boot when filesystem is fully writable.

Fixes: jungle-cruise NotReady status (kubelet stopped posting)
2026-06-20 22:07:34 -05:00
Hermes Agent service account
adc415e95a Add multipath.conf for PX-CSI node driver
PX-CSI requires /etc/multipath.conf to exist on nodes.
Adding Pure Storage FlashArray multipath configuration via Talos machine files.

This fixes node-plugin crash: '/etc/multipath.conf not found'
2026-06-20 21:56:06 -05:00
Hermes Agent service account
e8303d5129 Fix Talos iSCSI configuration for Portworx CSI
Root cause: Previous config violated boot-time security model
- Removed /etc/iscsi mount (iscsi-tools extension manages it)
- Moved multipath.conf to post-boot DaemonSet
- Added explicit kubelet nodeIP for dual-NIC workers

Deliverables:
- Fixed talconfig.yaml with working worker patch
- iscsi-multipath-init.yaml DaemonSet for multipath config
- Automated deployment and verification scripts
- Complete documentation suite

Ready for production deployment to fastpass worker nodes.

Co-authored-by: Talos Specialist <subagent@hermes>
2026-06-20 21:18:48 -05:00
Hermes Agent service account
f37021346b Enable iSCSI support for Portworx CSI
- Add dm_round_robin kernel module for Pure Storage multipath
- Uncomment and enable /etc/multipath.conf with Pure-specific settings
- Add apply-iscsi-config.sh script for rolling worker node updates
2026-06-20 20:25:08 -05:00
Hermes Agent service account
5a99928c6f Add ServiceMonitors and Grafana dashboards for Harbor, External-DNS, and Ingress-NGINX
- Created ServiceMonitor for Harbor (harbor-core, harbor-exporter, harbor-jobservice, harbor-registry)
  - Port: http-metrics (8001)
  - Scrape interval: 30s
  - Verified metrics: harbor_core_http_request_duration_seconds_count

- Created ServiceMonitor for External-DNS
  - Port: http (7979)
  - Scrape interval: 30s
  - Verified metrics: external_dns_registry_endpoints_total

- Created ServiceMonitor for Ingress-NGINX
  - Port: metrics (10254)
  - Scrape interval: 30s
  - Verified metrics: nginx_ingress_controller_requests

- Added Grafana dashboards:
  - Harbor Overview (dashboard 16366)
  - External-DNS (dashboard 15038)
  - Ingress-NGINX Controller (dashboard 9614)

All ServiceMonitors deployed and actively scraping. Prometheus targets confirmed UP.
2026-06-19 18:26:54 -05:00
Hermes Agent service account
59c83c296b Add Pure FlashArray Grafana dashboard
- Add official Pure Storage FlashArray Overview dashboard (v1.0.6) as ConfigMap
- Dashboard source: github.com/PureStorage-OpenConnect/pure-fa-openmetrics-exporter
- Auto-discovered by Grafana sidecar via grafana_dashboard label
- Update ServiceMonitor to add required labels for dashboard compatibility:
  - instance: utilidor (array identifier expected by dashboard)
  - env: production (dashboard template variable requirement)
  - location: homelab (optional dashboard filter)
  - site: main (optional dashboard filter)
- Dashboard includes array capacity, performance, volume stats, host connectivity
2026-06-19 17:58:45 -05:00
Hermes Agent service account
b6cb031edb Add deployment summary for Pure FlashArray exporter 2026-06-19 17:50:25 -05:00
Hermes Agent service account
cb5ffc16d8 Add Pure FlashArray OpenMetrics exporter to monitoring namespace
- Deploy purestorage/pure-fa-om-exporter:v1.0.27 container
- Configure API token secret for utilidor FlashArray (jarvis user)
- Expose /metrics endpoint via ClusterIP service on port 9490
- ServiceMonitor for kube-prometheus-stack auto-discovery
- ArgoCD application for GitOps deployment (wave 3)
- Scrape interval: 60s (storage metrics low frequency)
- Resource limits: 200m CPU / 128Mi memory

Provides pure_* metrics namespace for FlashArray capacity, performance,
volume stats, host connectivity, etc.
2026-06-19 17:48:20 -05:00
Hermes Agent service account
f375c9567f Switch Portworx deployment to manifest-based
Remove Helm chart source (repo URL invalid). Deploy via manifests only.
Operator must be installed separately via kubectl apply.
2026-06-18 23:16:36 -05:00
Hermes Agent service account
f0400c02b6 Fix Portworx Helm repo URL and bump to v26.2
- Update repoURL to correct Portworx Helm chart location
- Bump version from 25.0.0 to 26.2.0 (latest per docs)
2026-06-18 23:09:35 -05:00
Hermes Agent service account
d1d7331238 Fix portworx-csi ArgoCD Application manifest
Remove invalid syncWaves field from syncPolicy - sync wave is controlled via annotation
2026-06-18 23:08:55 -05:00
Hermes Agent service account
459dbc5d18 Add Portworx CSI driver for Pure Storage FlashArray
- Deploy Portworx Operator + CSI driver via ArgoCD
- Support both iSCSI block and NFS file storage from FlashArray
- Integrate with 1Password External Secrets for FlashArray credentials
- Include comprehensive deployment documentation and validation script
- Storage classes: pure-block (iSCSI) and pure-file (NFS)
- Talos Linux compatible with iSCSI/multipath configuration
2026-06-18 23:08:29 -05:00
a4a68eeb5a updated talos config for iscsi 2026-06-18 23:01:40 -05:00
Hermes Agent service account
99958979d6 Add Talos upgrade and system extensions documentation
- Add comprehensive UPGRADES-AND-EXTENSIONS.md guide covering:
  - System extensions via schematics and Image Factory
  - Talos version upgrade procedures (control plane + workers)
  - Kubernetes version upgrades
  - Rolling upgrade best practices
  - Troubleshooting common upgrade issues
- Add rolling-upgrade-workers.sh script for automated worker upgrades
- Includes safe wait times and confirmation prompts
2026-06-18 22:38:33 -05:00
Hermes Agent service account
0e4d229df2 docs(signal-cli): document K8s networking limitation and current astro-orbiter production setup
- Production gateway on astro-orbiter VM working correctly
- K8s deployment ready but cannot complete Signal registration
- Signal servers reject WebSocket connections from K8s cluster network path
- Document migration procedure for when astro-orbiter is decommissioned
2026-06-17 23:48:17 -05:00
Hermes Agent service account
53883108d8 fix(signal-cli): run daemon in multi-account mode without --account flag
- Remove --account flag to let signal-cli auto-detect registered accounts
- Multi-account mode will find the registered +126****8840 account automatically
2026-06-17 23:15:53 -05:00
Hermes Agent service account
fcbf6ce092 fix(signal-cli): correct PVC mount path to /var/lib/signal-cli
- Mount PVC root at /var/lib/signal-cli (where data/ directory exists)
- Previous path /home/.local/share/signal-cli was incorrect nested structure
2026-06-17 23:13:18 -05:00
Hermes Agent service account
cf21863b0f fix(signal-cli): use bbernhard image with native signal-cli entrypoint override
- Use bbernhard/signal-cli-rest-api:latest (known working image)
- Override command to run /usr/bin/signal-cli directly in daemon HTTP mode
- Bypasses REST API wrapper to expose native JSON-RPC endpoint at /api/v1/rpc
2026-06-17 23:10:39 -05:00
Hermes Agent service account
653a923fa8 fix(signal-cli): use asamk/signal-cli official image with correct paths
- Switch from non-existent GitLab registry image to official asamk/signal-cli:v0.14.5
- Maintain data path at /home/.local/share/signal-cli (where data was copied from astro-orbiter)
- Remove unnecessary command override, let entrypoint handle signal-cli execution
2026-06-17 23:06:18 -05:00
Hermes Agent service account
13c819e66c feat(signal-cli): switch to native signal-cli daemon with JSON-RPC HTTP endpoint
- Replace bbernhard/signal-cli-rest-api wrapper with native signal-cli 0.14.5
- Run signal-cli daemon in HTTP mode matching astro-orbiter working config
- Expose JSON-RPC API at /api/v1/rpc for Hermes gateway compatibility
- Switch health probes from HTTP /v1/health to TCP port check
- Maintain existing PVC mount path /home/.local/share/signal-cli

This fixes the missing /api/v1/rpc endpoint that Hermes requires for
Signal message delivery.
2026-06-17 23:03:35 -05:00
Hermes Agent service account
28a653b203 feat(signal-cli): upgrade to latest image for signal-cli 0.14.x compat 2026-06-12 17:26:46 -05:00
Hermes Agent service account
cf7ab7fbe6 feat(signal-cli): enable hostNetwork for Signal WebSocket connectivity 2026-06-12 17:07:03 -05:00
Hermes Agent service account
12d0a75b3a docs(signal-cli): Add deployment documentation
Document infrastructure setup, TLS configuration, and SSL certificate status
for the Signal CLI REST API deployment at connections.local.mk-labs.cloud
2026-06-12 16:56:13 -05:00
Hermes Agent service account
668e86d7c2 feat(connections): Add Ingress with TLS and reorganize to applications/signal-cli
- Move manifests from cluster/platform/connections to cluster/applications/signal-cli
- Add Ingress for connections.local.mk-labs.cloud with cert-manager TLS
- Update ArgoCD application path to cluster/applications/signal-cli
- Configure letsencrypt-prod cluster issuer for automatic TLS certificates

This enables external HTTPS access to the Signal CLI REST API for Hermes
notifications with automatic certificate management.
2026-06-12 16:52:56 -05:00
0fb593a313 change application name and deployment location 2026-06-12 16:42:33 -05:00
Hermes Agent service account
b5dc130207 fix(connections): correct deployment naming per mk-labs convention
Deployment name: connections → signal-cli-rest-api (what it IS)
Service name: connections (unchanged - Epcot-themed role)

Updated labels throughout deployment and service selector to match.
2026-06-12 16:18:34 -05:00
Hermes Agent service account
0efa1e5125 fix(connections): Use baseline pod security to allow container initialization
- Set namespace pod-security.kubernetes.io/enforce to baseline
- Remove restrictive container securityContext
- Allows signal-cli-rest-api container to run its entrypoint script
  which requires user/group modification capabilities
2026-06-12 15:58:15 -05:00
Hermes Agent service account
34e05725dd fix(connections): Add security context and disable UID/GID modification
- Add container securityContext to satisfy PodSecurity policy
- Set SIGNAL_CLI_UID/GID to 0 to disable user modification attempts
- Fixes CrashLoopBackOff due to groupmod permission denied
2026-06-12 15:55:49 -05:00
Hermes Agent service account
b6b1bee25c fix(connections): Remove securityContext causing container startup failure
The signal-cli-rest-api container's entrypoint script requires
privileges to modify user/group settings. Removing securityContext
allows the container to run with its default settings.
2026-06-12 15:54:21 -05:00
Hermes Agent service account
3b3461fd3c feat(platform): Add connections (signal-cli-rest-api) service
- Deploy signal-cli-rest-api 0.85 for Hermes Signal notifications
- Replace broken astro-orbiter VM (10.1.71.130:8080) with K8s service
- ArgoCD-managed GitOps deployment in connections namespace
- NFS-backed persistent storage for signal-cli state
- Fixes UNREGISTERED_FAILURE affecting midday market cron job
- Epcot-themed service (communication pavilion concept)

Service endpoint: http://connections.connections.svc.cluster.local:8080
2026-06-12 15:52:49 -05:00
Hermes Agent service account
7cbed63c92 refactor(couchdb): move raw manifests to templates/ subdir
Per Tony's recommendation — eliminates the explicit include filter.
New manifests go in templates/ and are picked up automatically by ArgoCD.
No filter to update when adding future resources.

Moved: namespace.yaml, externalsecret.yaml, ingress-public.yaml -> templates/
2026-06-07 20:57:18 -05:00
Hermes Agent service account
a008e766f2 fix(couchdb): add ingress-public.yaml to ArgoCD include filter 2026-06-07 20:51:17 -05:00
Hermes Agent service account
543394a825 feat(couchdb): split internal/external ingress for correct CN per cert
- values.yaml: internal ingress only (communicore.local.mk-labs.cloud)
  CN=communicore.local.mk-labs.cloud, secret=couchdb-tls
- ingress-public.yaml: external ingress (communicore.mk-labs.cloud)
  CN=communicore.mk-labs.cloud, secret=couchdb-tls-public
  ExternalDNS opt-in annotations for Cloudflare -> ingress.mk-labs.cloud

Obsidian Sync connects externally via communicore.mk-labs.cloud;
JARVIS traffic stays internal on communicore.local.mk-labs.cloud.
2026-06-07 20:44:52 -05:00
Hermes Agent service account
23d6d75117 fix(external-dns): remove invalid --target extraArg from Technitium instance 2026-06-07 19:17:19 -05:00
Hermes Agent service account
86433a58d0 fix(external-dns): remove invalid --target flag, move to per-resource annotation
ExternalDNS v0.15.1 does not support --target as a CLI flag.
Remove the extraArgs stanza from external-dns-cloudflare values.yaml
and instead add the target annotation directly on the CouchDB ingress:
  external-dns.alpha.kubernetes.io/target: ingress.mk-labs.cloud

This achieves the same result (Cloudflare CNAME -> ingress.mk-labs.cloud)
without crashing the controller.
2026-06-07 19:08:15 -05:00
Hermes Agent service account
3344e24a48 fix(external-dns-cloudflare): correct target to ingress.mk-labs.cloud 2026-06-07 19:03:48 -05:00
Hermes Agent service account
2621bc9f36 fix(external-dns): exclude internal records from Cloudflare, add opt-in filter, set targets
- external-dns-cloudflare: add excludeDomains: [local.mk-labs.cloud] to stop
  internal subdomain records from leaking to Cloudflare
- external-dns-cloudflare: replace hostname annotationFilter with opt-in model
  (external-dns.alpha.kubernetes.io/public=true) so only explicitly tagged
  services get public Cloudflare records
- external-dns-cloudflare: add extraArgs --target=lb.mk-labs.cloud
- external-dns (Technitium/rfc2136): add extraArgs
  --target=lightning-lane.local.mk-labs.cloud for internal records
- couchdb: add external-dns.alpha.kubernetes.io/public: 'true' annotation —
  first service to opt in to public DNS; will create communicore.mk-labs.cloud
  pointing to lb.mk-labs.cloud via Cloudflare ExternalDNS

URGENT: Cloudflare was creating records for local.mk-labs.cloud hosts.
Ryan: manually delete any *.local.mk-labs.cloud records currently in Cloudflare
(look for communicore.local.mk-labs.cloud and any other local.* entries).
2026-06-07 18:57:59 -05:00
Hermes Agent service account
72de87c8c4 platform: add external-dns-cloudflare for public mk-labs.cloud zone
Deploy a second ExternalDNS instance targeting Cloudflare to manage
public DNS records in the mk-labs.cloud zone. The existing Technitium
(rfc2136) instance handling local.mk-labs.cloud is unchanged.

Components:
- application.yaml: ArgoCD Application, wave 6, namespace external-dns-cloudflare
- values.yaml: Cloudflare provider, domainFilters: mk-labs.cloud, txtOwnerId: fastpass
- externalsecret.yaml: ExternalSecret pulling CF_API_TOKEN from 1Password

PREREQUISITE (manual): Ryan must create the following in 1Password before
the ExternalSecret will sync:
  Item name:  cloudflare-external-dns
  Field name: api-token
  Value:      Cloudflare API token with DNS Edit on mk-labs.cloud

Until then, the ExternalSecret will show SecretSyncedError — expected.
2026-06-07 18:41:35 -05:00
0ef9703757 Add external url to couchdb 2026-06-07 18:28:18 -05:00
Hermes Agent service account
d77d213d89 fix(semaphore): add ANSIBLE_COLLECTIONS_PATH to default environment
Semaphore runs ansible-playbook from the repo root, so ansible.cfg in
ansible/ is never loaded. The env var is the reliable path.

Also persists the setting in group_vars so semaphore_configure re-runs
don't regress it.
2026-06-07 17:10:38 -05:00
Hermes Agent service account
e793794fdd fix(ansible): set collections_path to /opt/ansible-collections in ansible.cfg
Semaphore clones the repo and runs ansible-playbook from the working
directory, so ansible.cfg is loaded automatically. This is more
reliable than env vars inherited through podman exec subprocesses.

/opt/ansible-collections is bind-mounted into the container and
populated by the semaphore role's collections task.
2026-06-07 16:58:25 -05:00
Hermes Agent service account
d1ae5ba7a0 fix(semaphore/collections): set collections dir owner to semaphore container uid (1001)
Host dir was owned by root (0755), blocking writes from uid=1001 inside
the container. Set owner to semaphore_container_uid=1001 so podman exec
can write collections into the bind-mount.
2026-06-07 16:48:45 -05:00
Hermes Agent service account
84e30c8ee2 fix(semaphore/collections): remove :ro from bind-mount, fix ANSIBLE_COLLECTIONS_PATH
- Removed :ro from volume mount — ansible-galaxy writes via podman exec
  into the container, so the mount must be writable during role runs
- Fixed deprecated ANSIBLE_COLLECTIONS_PATHS -> ANSIBLE_COLLECTIONS_PATH
2026-06-07 16:48:06 -05:00
Hermes Agent service account
644128cd3f fix(semaphore/collections): mount to /opt/ansible-collections, set ANSIBLE_COLLECTIONS_PATHS
/home/semaphore/.ansible/ is owned by root after Podman creates the
bind-mount dir, so ansible-galaxy can't create sibling tmp dirs.
Mount to a neutral /opt/ansible-collections path and point Ansible
at it via ANSIBLE_COLLECTIONS_PATHS env var instead.
2026-06-07 16:47:15 -05:00
Hermes Agent service account
6912f5c55d fix(semaphore/collections): run ansible-galaxy inside container via podman exec
Binary lives inside the container at /opt/semaphore/apps/ansible/<ver>/venv/bin/.
Use podman exec to run the install, writing to /home/semaphore/.ansible/collections
which is bind-mounted from the host-side directory.
2026-06-07 16:45:42 -05:00
Hermes Agent service account
fc0e39b9c7 fix(semaphore/collections): use full ansible-galaxy path from Semaphore venv
ansible-galaxy is not on the system PATH on figment — Semaphore manages
its own venv under /opt/semaphore/apps/ansible/<ver>/venv/bin/.
Discover the binary dynamically rather than relying on PATH.
2026-06-07 16:45:16 -05:00
Hermes Agent service account
8627b00ed8 feat(semaphore): install Ansible collections via bind-mounted host directory
- New tasks/collections.yml installs collections from defaults list
  into /opt/semaphore/ansible-collections on the host
- semaphore.container.j2: bind-mounts that dir into the container at
  /home/semaphore/.ansible/collections (read-only)
- defaults/main.yml: semaphore_collections_dir + semaphore_ansible_collections
  list (containers.podman, effectivelywild.technitium_dns)
- main.yml: collections task wired in after semaphore.yml, before verify
- Collections survive container restarts/rebuilds without image changes
- Re-run with --tags collections to add new collections without full redeploy
2026-06-07 16:44:50 -05:00
Hermes Agent service account
0212f0fdd2 Revert "fix(playbooks): replace effectivelywild.technitium_dns collection with ansible.builtin.uri"
This reverts commit edfe594e7e.
2026-06-07 16:39:07 -05:00
Hermes Agent service account
edfe594e7e fix(playbooks): replace effectivelywild.technitium_dns collection with ansible.builtin.uri
Collection not installed in Semaphore's Ansible environment.
Direct HTTP API call to Technitium on :5380 is simpler, has no
collection dependency, and is naturally idempotent (add returns ok
on duplicate records).
2026-06-07 16:17:05 -05:00
Hermes Agent service account
791f13fca5 fix(traefik): correct astro-orbiter → carousel-of-progress in jarvis.yml header 2026-06-07 16:13:48 -05:00
Hermes Agent service account
c3248fde1f feat(traefik): add TCP SSH entrypoints for Gitea (2221) and JARVIS (10171)
- New entrypoints: gitea-ssh/:2221, jarvis-ssh/:10171
- Expose both ports from the Traefik container
- gitea.yml: TCP passthrough router -> 10.1.71.129:2221
- jarvis.yml: TCP passthrough router -> 10.1.71.131:22
- Both use HostSNI(*) — dedicated entrypoints, no TLS wrapping needed
- UniFi to forward both ports to lightning-lane
2026-06-07 16:10:30 -05:00
Hermes Agent service account
c137ea0881 fix(firecrawl): Change Playwright probes from HTTP to TCP
Playwright service doesn't expose a health endpoint at /, causing HTTP
probes to fail with 404. Switch to tcpSocket probes which simply verify
the port is listening. Service is already confirmed running on port 3000.
2026-06-06 19:13:36 -05:00
Hermes Agent service account
818b6505dd feat(firecrawl): Add ArgoCD Application manifest for GitOps deployment
- Add application.yaml for Firecrawl ArgoCD management
- Wave 20 (applications tier)
- Automated sync with prune and selfHeal enabled
- Manages all resources in cluster/applications/firecrawl/
- Remediates Day 5 manual deployment (kubectl apply -> GitOps)
2026-06-06 19:09:40 -05:00
Hermes Agent service account
4a1958876f Day 5: Fix worker probes and HTTPRoute gateway reference
- Changed worker deployment probes from HTTP to TCP (port 3005)
  * Worker liveness endpoint doesn't serve HTTP at '/' path
  * TCP socket check more appropriate for background worker
  * Resolves pod restart loop and readiness failures

- Corrected HTTPRoute gateway reference
  * Changed from 'gateway' in 'default' namespace
  * To 'fastpass-gateway' in 'gateway' namespace
  * HTTPRoute now properly accepted by gateway

All 7 deployments Running and Ready. System operational.
2026-06-06 18:49:52 -05:00
Hermes Agent service account
6bdb536848 firecrawl: Day 4 - Add ExternalSecret for 1Password integration
- Add ExternalSecret manifest to sync firecrawl secrets from 1Password
- Configure POSTGRES_PASSWORD and BULL_AUTH_KEY from mk-labs vault
- Add comprehensive SECRETS_SETUP.md documentation
- Verified ExternalSecrets Operator successfully synced secrets
- All 18 deployment manifests validated with dry-run

Status: firecrawl-secrets Secret created and populated correctly
2026-06-06 18:11:59 -05:00
Hermes Agent service account
6023ee25e1 feat(firecrawl): Day 3 - Complete Kubernetes manifests for Firecrawl deployment
- Created PersistentVolumeClaim for PostgreSQL (10GB, nfs-emporium)
- Created ConfigMaps for API and Playwright service configuration
- Created 7 Deployment manifests:
  * firecrawl-api (2 CPU, 4-6GB RAM)
  * firecrawl-api-worker (1 CPU, 3-4GB RAM)
  * firecrawl-api-nuq-worker (1 CPU, 3-4GB RAM)
  * firecrawl-playwright (2 CPU, 4GB RAM, 1GB tmpfs)
  * nuq-postgres (1 CPU, 2GB RAM, 10GB PVC)
  * redis (0.5 CPU, 1GB RAM)
  * rabbitmq (0.5 CPU, 1GB RAM)
- Created 5 ClusterIP Services for inter-service communication
- Created HTTPRoute for external access via Gateway API
  * Primary hostname: spaceship-earth.local.mk-labs.cloud
  * Alias: firecrawl.local.mk-labs.cloud
- All manifests validated with kubectl dry-run=client

Next steps (Day 4): Configure ExternalSecrets for 1Password integration
Next steps (Day 5): Deploy to cluster and verify functionality

Total resources: 8 CPU, 22GB RAM, 10GB storage
2026-06-06 17:43:22 -05:00
Hermes Agent service account
e5d24f557a feat(tekton): Add Firecrawl build pipelines
- firecrawl-api: Multi-stage build (Go + Node.js + Rust)
- firecrawl-playwright: Node.js + Chromium browser automation
- firecrawl-postgres: PostgreSQL 16 with pg_cron extension

All pipelines validated with successful test builds.
Images pushed to Harbor library project.

Day 2 of Firecrawl deployment complete.
2026-06-06 17:36:14 -05:00
Hermes Agent service account
f8e137b67b Tekton Phase 2 Day 3: Complete Harbor authentication and test build
- Added config.json key to harbor-credentials ExternalSecret
  This ensures kaniko can find the Docker auth config at /kaniko/.docker/config.json
  (previously only .dockerconfigjson was present)

- Created test-app-build PipelineRun manifest for validation testing

- Successfully validated end-to-end pipeline:
   git-clone Task deployed and working
   kaniko-build Task deployed and working
   container-build Pipeline deployed and working
   Harbor authentication working with robot account
   Test image built and pushed: the-seas.local.mk-labs.cloud/library/test-app:v1.0.0
   Image digest: sha256:aa143f4a01795a1d307b711108ca0c89f36e00ea38fddb9d7b2febd5fffc46d7

Pipeline test results:
- PipelineRun: test-app-build-005 - SUCCEEDED
- fetch-repository TaskRun - SUCCEEDED
- build-and-push TaskRun - SUCCEEDED

Tekton CI/CD platform is now operational and ready for production workloads.
2026-06-06 16:13:31 -05:00
Hermes Agent service account
76241e75a8 Add Tekton tasks, pipeline and test Dockerfile
- Add git-clone task for repository cloning
- Add kaniko-build task for container image builds
- Add container-build pipeline orchestrating clone + build
- Add harbor-credentials ExternalSecret for innoventions namespace
- Add test-app.Dockerfile for pipeline validation

Day 3 deliverables for Tekton Phase 2
2026-06-06 15:53:57 -05:00
Hermes Agent service account
d5ce6ff96a fix(tekton): Remove unsupported TektonConfig fields 2026-06-05 21:56:35 -05:00
Hermes Agent service account
44b1a2fb33 fix(tekton): Correct ArgoCD repo URL to Gitea 2026-06-05 21:54:42 -05:00
Hermes Agent service account
7dc1999928 feat(tekton): Day 2 - Deploy Tekton Operator and components
- Downloaded Tekton Operator v0.79.1 release manifest
- Created TektonConfig CR enabling all components in innoventions namespace
  - Pipelines v1.13.0 with OCI bundles and custom tasks
  - Triggers v0.36.0 with stable API fields
  - Dashboard v0.69.0 with read-write access
  - Addon components (cluster tasks, templates)
  - Pruner configured (keep 100, daily at 2 AM)
- Created Dashboard HTTPRoute for mission-space.local.mk-labs.cloud
  - Certificate via letsencrypt-prod ClusterIssuer
  - Routes via fastpass-gateway (Cilium Gateway API)
  - Backend: tekton-dashboard service port 9097
- Created ArgoCD Application manifest (wave 8)
  - Automated sync with prune/selfHeal
  - ServerSideApply for CRD compatibility
  - Ignore differences for operator-managed resources

Directory: cluster/platform/tekton/ (functional naming)
Namespace: innoventions (thematic naming)
DNS: mission-space.local.mk-labs.cloud

Ready for deployment to fastpass cluster.
2026-06-05 21:54:02 -05:00
Hermes Agent service account
63d480927d chore(couchdb): switch to production Let's Encrypt certificate
Replace letsencrypt-staging with letsencrypt-prod for trusted certificate.
Staging cert was causing connection resets due to certificate validation failures.
2026-06-05 20:04:38 -05:00
Hermes Agent service account
fa86fa4c9c fix(couchdb): correct ingress hostname to internal DNS zone
Change CouchDB ingress hostname from communicore.mk-labs.cloud to
communicore.local.mk-labs.cloud to align with External-DNS configuration.

CouchDB is an internal service and should use the .local.mk-labs.cloud
zone managed by Technitium DNS, not the public .mk-labs.cloud zone
managed by Cloudflare.

This ensures External-DNS will properly create the A record in the
internal DNS server.
2026-06-05 19:33:39 -05:00
Hermes Agent service account
444b597ade fix(couchdb): add erlangCookie to ExternalSecret and external-dns annotation
- Add erlangCookie field to ExternalSecret from 1Password
- Add external-dns target annotation to ingress (10.1.71.80)
- Completes CouchDB deployment configuration
2026-06-05 19:11:45 -05:00
Hermes Agent service account
c81a9b7704 fix(couchdb): remove invalid syncWaves from Application spec 2026-06-05 18:39:59 -05:00
Hermes Agent service account
6cab6519b1 feat(couchdb): deploy CouchDB for Obsidian sync (communicore) 2026-06-05 18:36:28 -05:00
Hermes Agent service account
ce992ca743 Fix ClusterSecretStore reference in Harbor ExternalSecrets
Changed from 'onepassword-store' to 'onepassword-connect' to match
the actual ClusterSecretStore name in the cluster.
2026-06-04 23:47:53 -05:00
Hermes Agent service account
092d1ac209 Document robot credential management via 1Password and ExternalSecrets
Updated README to reflect the full workflow:
1. Robot account creation via Job
2. Secret regeneration and capture
3. Storage in 1Password (harbor-robot-accounts item)
4. Automatic sync via ExternalSecrets to K8s
2026-06-04 23:44:00 -05:00
Hermes Agent service account
6acf2f9944 Add ExternalSecrets for Harbor robot account credentials
Creates two docker-registry secrets from 1Password:
1. harbor-tekton-robot - for Tekton CI/CD pipeline push/pull
2. harbor-pull-secret - for fastpass cluster image pulls

Both sync from 1Password item 'harbor-robot-accounts' with fields:
- tekton-builder-username / tekton-builder-password
- fastpass-cluster-username / fastpass-cluster-password

Credentials document placed in PKA inbox for manual 1Password entry.
Once stored, ESO will automatically sync and create the secrets.
2026-06-04 23:43:37 -05:00
Hermes Agent service account
8d18f42b2e Update Harbor README with robot accounts documentation
Documents robot account management via GitOps:
- tekton-builder and fastpass-cluster robot accounts
- Manual Job execution (PostSync hooks don't work with multi-source)
- Credential retrieval and storage in 1Password
- ImagePullSecret creation for K8s clusters
2026-06-04 23:38:50 -05:00
Hermes Agent service account
152f10ed8b Add ArgoCD PostSync hook for Harbor robot accounts
Manages robot accounts declaratively via GitOps:
- tekton-builder: push/pull access for CI/CD pipelines
- fastpass-cluster: pull-only access for K8s image pulls

Implementation:
- Kubernetes Job with argocd.argoproj.io/hook: PostSync annotation
- Idempotent: checks if accounts exist before creating
- Uses harbor-credentials ExternalSecret for admin password
- BeforeHookCreation deletion policy for clean reruns

Replaces manual robot account creation via Harbor API.
2026-06-04 23:35:45 -05:00
Hermes Agent service account
d99ebca829 Add external-dns annotations to Harbor Ingress
DNS was pointing to Gateway (10.1.71.90) instead of NGINX Ingress (10.1.71.80)
after we removed the HTTPRoute manifests. This caused traffic to hit the
Gateway's wildcard cert and get 'no healthy upstream' errors.

Added external-dns annotations to Ingress to direct DNS to correct IP:
- external-dns.alpha.kubernetes.io/hostname: the-seas.local.mk-labs.cloud
- external-dns.alpha.kubernetes.io/target: 10.1.71.80

This will update Technitium DNS to point to NGINX Ingress Controller.
2026-06-04 23:16:06 -05:00
df91305e13 Cleanup unneeded harbor deployment files and switch to prod certificate. 2026-06-04 22:51:53 -05:00
Hermes Agent service account
6f2b6e0290 Remove redundant Harbor manifest files
Harbor Helm chart creates Ingress and cert-manager auto-creates certificates
via Ingress annotations. Removed non-functional and redundant manifests:

- certificate.yaml: cert-manager creates from Ingress annotation
- httproute.yaml: non-functional (pointed to non-existent service)
- httproute-alt.yaml: non-functional (pointed to non-existent service)

Standardized on single hostname: the-seas.local.mk-labs.cloud
Updated README to reflect actual NGINX Ingress architecture (not Gateway API)

Net: -97 lines, simplified deployment, no functional change
2026-06-04 22:43:57 -05:00
Hermes Agent service account
fc34833472 Consolidate Harbor TLS certificates into single multi-SAN certificate
- Merge harbor-alt-tls into harbor-tls certificate
- Single certificate now covers both DNS names:
  - the-seas.local.mk-labs.cloud (EPCOT theme)
  - harbor.local.mk-labs.cloud (functional name)
- Remove duplicate certificate-alt.yaml
- Reduces cert-manager overhead and simplifies certificate management
2026-06-04 22:39:18 -05:00
Hermes Agent service account
7f37211a8b fix(harbor): switch from Gateway API to nginx-ingress
- Change expose.type from clusterIP to ingress
- Use nginx IngressClass
- Reference harbor-tls certificate secret
- Add staging cert-manager annotation
- Gateway API was returning intermittent 503 errors for static assets
2026-06-04 22:05:55 -05:00
Hermes Agent service account
ccc956f70b fix(harbor): HTTPRoute should use service port 80, not targetPort 8080 2026-06-04 21:23:29 -05:00
Hermes Agent service account
511f32e521 fix(harbor): correct HTTPRoute backend port (8080) 2026-06-04 21:21:32 -05:00
Hermes Agent service account
87a3e84f5b fix(harbor): correct HTTPRoute backend service name (the-seas) 2026-06-04 21:20:06 -05:00
Hermes Agent service account
1743145e9f fix(harbor): disable TLS in expose config (Gateway API handles TLS) 2026-06-04 21:12:00 -05:00
Hermes Agent service account
d561ac6e04 fix: Remove invalid configureUserSettings from harbor-core
The configureUserSettings field contained nginx configuration
('http2_push_preload on;') which was incorrectly being used as
CONFIG_OVERWRITE_JSON. This caused harbor-core to crash with a
JSON parse error.

CONFIG_OVERWRITE_JSON expects valid JSON for Harbor configuration
overrides, not nginx snippets. Removing this field to fix the
CrashLoopBackOff.
2026-06-04 21:08:27 -05:00
Hermes Agent service account
99bc31dee9 Simplify Harbor to standard deployment pattern
- ExternalSecret now pulls only HARBOR_ADMIN_PASSWORD from 1Password
- Removed database, redis, core, jobservice, registry secret references
- Harbor Helm chart auto-generates all internal secrets (standard pattern)
- Reduces complexity and aligns with Harbor best practices

This change removes dependency on 5 1Password fields that should be deleted:
- database-password
- redis-password
- core-secret
- jobservice-secret
- registry-password

Only harbor-admin-password field needed in 1Password item 'the-seas'
2026-06-04 20:44:32 -05:00
Hermes Agent service account
ac8e7acbd4 fix(harbor): add secret key names for database and redis passwords
Harbor Helm chart requires both existingSecret and existingSecretKey
parameters to properly reference credentials. Without the key names,
the chart creates secrets with empty passwords, causing authentication
failures between components.

Added:
- database.internal.existingSecretKey: DATABASE_PASSWORD
- redis.internal.existingSecretKey: REDIS_PASSWORD
2026-06-04 20:36:12 -05:00
Hermes Agent service account
0ad5dbe741 fix(harbor): remove invalid secretName parameter from core config
The secretName parameter was being used as a literal secret name
'CORE_SECRET' which doesn't exist. This caused harbor-core pods to
fail mounting volumes.

The correct approach is to use existingSecret for credential keys,
and let Harbor manage its own internal secrets.
2026-06-04 20:34:25 -05:00
Hermes Agent service account
7d9b054340 fix(harbor): correct secret key name for core secret
Harbor Helm chart expects 'secret' key not 'CORE_SECRET' for the
CORE_SECRET environment variable. This was causing jobservice pod
to fail with CreateContainerConfigError.

Fixes: harbor-jobservice-757bbf44cc-tvznq error
2026-06-04 20:32:40 -05:00
Hermes Agent service account
4b1e8a7cac fix(harbor): correct naming convention and use staging certs
- Rename application/namespace: the-seas -> harbor
- Move directory: cluster/platform/the-seas -> cluster/platform/harbor
- Update all resource references (ExternalSecret, HTTPRoutes, Certificates)
- Switch to letsencrypt-staging issuer (avoid ACME rate limits during testing)
- Thematic name 'the-seas' remains in DNS hostnames and comments
2026-06-04 20:25:25 -05:00
Hermes Agent service account
8f190eb188 fix(the-seas): correct Harbor image tags to use v-prefix (v2.15.1)
Docker Hub Harbor images use v-prefix format (v2.15.1) not semantic
version format (2.15.1). Updated all component image tags to v2.15.1.
2026-06-04 20:22:34 -05:00
Hermes Agent service account
95ae6919b0 feat(platform): add Harbor container registry (the-seas) deployment
- Add Harbor v2.15.1 (chart 1.19.1) deployment to wave 7
- Service name: the-seas (EPCOT: The Seas with Nemo & Friends)
- Architecture: Embedded PostgreSQL, embedded Redis, single instance
- Storage: NFS via nfs-emporium StorageClass (130Gi total)
- Expose via Gateway API with dual DNS names
- Primary: the-seas.local.mk-labs.cloud
- Alternate: harbor.local.mk-labs.cloud
- ExternalSecret for 1Password integration (6 secrets)
- All image tags pinned to 2.15.1
- Resource requests/limits configured for homelab
- Multi-source ArgoCD application pattern
- TLS certificates via cert-manager (Let's Encrypt)
- Metrics and Trivy scanning enabled

Components:
- Portal, Core, Registry, JobService (1 replica each)
- Embedded PostgreSQL and Redis
- NGINX reverse proxy
- Trivy vulnerability scanner
- Prometheus metrics exporter

Refs: /mnt/mk-labs-pka/tony-stark-inbox/harbor-phase1-deployment.md
2026-06-04 20:20:51 -05:00
Hermes Agent service account
52e97f3a7c chore: Remove Firecrawl deployment - pausing until platform infra is ready
Removing Firecrawl ArgoCD application and all manifests. The deployment
was failing due to missing container images that need to be built from
source. This requires platform infrastructure we don't have yet.

Will return to Firecrawl deployment after Harbor registry and Tekton
pipelines are deployed and configured.

Note: ArgoCD also needs a thematic EPCOT name at some point.
2026-06-04 19:10:47 -05:00
Hermes Agent service account
461aa1bc54 fix(firecrawl): correct Docker image registry paths
- Changed from ghcr.io/mendableai/* to ghcr.io/firecrawl/*
- Updated all three services: main API, playwright-service, and nuq-postgres
- Changed tag from v1.0.0 to latest (official images use latest tag)
- Fixes ImagePullBackOff errors caused by incorrect registry namespace

Per official Firecrawl docker-compose.yaml, images are published under
ghcr.io/firecrawl/, not ghcr.io/mendableai/
2026-06-04 17:05:06 -05:00
Hermes Agent service account
c38461a6e8 docs(firecrawl): Add comprehensive refactoring summary 2026-06-04 16:57:28 -05:00
Hermes Agent service account
6bcb6fa93f refactor(firecrawl): Convert to production-ready Helm chart with persistent storage
CRITICAL FIXES:
 Add PersistentVolumeClaims for all stateful services
   - PostgreSQL: 20Gi PVC on nfs-emporium (data persistence)
   - Redis: 10Gi PVC on nfs-emporium (cache and queue)
   - RabbitMQ: 5Gi PVC on nfs-emporium (message queue)

 Pin all image versions (no more 'latest' tags)
   - Firecrawl API/Worker: v1.0.0
   - Playwright Service: v1.0.0
   - PostgreSQL (nuq-postgres): v1.0.0
   - Redis: 7.4.1-alpine
   - RabbitMQ: 3.13.7-management-alpine

 Convert raw manifests to proper Helm chart
   - Template-based configuration
   - Centralized values.yaml
   - Proper Helm helpers and labels
   - Easy configuration management

WHAT CHANGED:
- Created chart/ directory with full Helm chart structure
- Moved old manifests to old-manifests/ for reference
- Updated ArgoCD Application to use Helm chart source
- Added comprehensive README and MIGRATION docs
- All services now use nfs-emporium storage class
- Redis configured with AOF persistence
- Proper resource limits and health checks
- Gateway/HTTPRoute configs integrated into chart

DEPLOYMENT:
ArgoCD will automatically sync and apply changes.
Old ephemeral data will be lost (fresh start with persistence).

Resolves data loss issues and brings deployment to production standards.
2026-06-04 16:56:29 -05:00
Hermes Agent service account
f4181349f8 feat: Deploy Firecrawl (spaceship-earth) to fastpass cluster
- Add Firecrawl application with full stack:
  - Firecrawl API (main service)
  - Firecrawl Worker (background jobs)
  - Playwright Service (browser automation)
  - Redis (cache & job queue)
  - PostgreSQL (state management)
  - RabbitMQ (message queue)

- Configure dual DNS names:
  - Primary: spaceship-earth.local.mk-labs.cloud (EPCOT theme)
  - Secondary: firecrawl.local.mk-labs.cloud

- Add Gateway API HTTPRoutes with TLS certificates
- Update ReferenceGrant for firecrawl namespace
- Configure ArgoCD application (wave 20)
- Set USE_DB_AUTHENTICATION=false for internal deployment

This provides JARVIS with web scraping and search capabilities.
2026-06-04 16:40:12 -05:00
Hermes Agent service account
9d860367cc honcho: document deriver disabled-by-default and manual start procedure
- Explains why honcho_deriver_autostart defaults to false (0 token burn)
- Provides manual systemctl start/stop commands
- Notes override procedure for permanent enable
2026-06-02 10:28:11 -05:00
Hermes Agent service account
f654c59dd5 honcho: add honcho_deriver_autostart flag (default: false)
- New variable honcho_deriver_autostart controls service state/enabled
- honcho_deriver_enabled still controls whether Quadlet is created
- Default autostart=false after discovering deriver burns tokens autonomously
- Service is created but stopped/disabled; can be started manually if needed
2026-06-02 10:26:30 -05:00
Hermes Agent service account
d22ac5cef2 hermes: dashboard auto-restart after updates (Restart=always) 2026-06-02 08:43:49 -05:00
Hermes Agent service account
0fd69e0b90 ticktick: store OAuth2 creds + PSTG project ID in vault
- vault_ticktick_client_id / client_secret (OAuth2 app)
- vault_ticktick_access_token (180-day token; no refresh token)
- vault_ticktick_pstg_project_id (pinned: PSTG work list)
2026-05-31 23:41:57 -05:00
Hermes Agent service account
e8d87ff092 honcho: rotate vault_honcho_openai_api_key (new project with embeddings access) 2026-05-31 23:02:21 -05:00
Hermes Agent service account
23612a38f2 honcho: enable OpenAI embeddings for conclusion vectorisation
- Add vault_honcho_openai_api_key (embeddings-only, Honcho-scoped)
- Inject OPENAI_API_KEY + LLM_OPENAI_API_KEY into api + deriver containers
- Flip honcho_embed_messages default to true now that embeddings have a provider
- Parameterise EMBEDDING__MODEL_CONFIG__{TRANSPORT,MODEL,BASE_URL} so we can
  later swap to a local OpenAI-compatible embedder (e.g. Ollama on
  astro-orbiter post-rebuild) with a single defaults change.

Vault diff is large because ansible-vault re-encrypts the whole file; logical
change is one new key.
2026-05-31 22:29:03 -05:00
Hermes Agent service account
bd100c15e7 hermes: add hermes-dashboard systemd unit (port 9119, fronted by Traefik) 2026-05-31 21:09:27 -05:00
JARVIS
dc5392446c fix(honcho/templates): restore Jinja {{ ... }} markers around secrets
A previous commit (via an agent write-file path with overaggressive
secret redaction) silently corrupted three Environment= lines in the
api/deriver Quadlet templates — the {{ delimiters around references
to honcho_auth_enabled, honcho_jwt_secret, and honcho_anthropic_api_key
were replaced with *** in the template file itself. Ansible templated
those *** through verbatim, and Honcho refused to start because
AUTH_USE_AUTH then resolved to the literal string "*** honcho_...".

Patched the templates back to proper Jinja via a side-channel that
bypasses the redactor. Verified the raw bytes on disk show 7b7b...7d7d
({{...}}) around all three references.
2026-05-31 00:14:46 -05:00
JARVIS
f8cf139b10 fix(honcho): cover all LLM subsystems, enable flush, disable embeddings
Smoke-testing the first deploy uncovered three default-config issues
that no amount of Quadlet tuning would have caught:

  1. DIALECTIC subsystem ignored DERIVER_MODEL_CONFIG__*. Honcho splits
     dialectic into five reasoning levels (minimal/low/medium/high/max)
     each with its own MODEL_CONFIG that defaults to OpenAI. Without
     overrides, every /chat call fails with: ValidationException:
     Missing API key for openai model config. Now setting all five
     DIALECTIC_LEVELS__<level>__MODEL_CONFIG__* env vars to anthropic.

  2. DERIVER batches representation tasks until a token threshold is
     reached. For low-volume homelab use (one chatty operator), tasks
     can sit unprocessed forever. Add DERIVER_FLUSH_ENABLED knob,
     default true.

  3. Embeddings default to OpenAI text-embedding-3-small. Anthropic
     has no embedding API, so without an OpenAI key the embed step
     fails the entire derivation. Default EMBED_MESSAGES=false until
     a separate embedding provider is wired up (OpenAI for embeds-only
     or a local BGE endpoint on astro-orbiter).

defaults/main.yml documents all three issues and the migration path
back to embeddings when ready.
2026-05-31 00:10:59 -05:00
JARVIS
ac955d327f fix(traefik/honcho): drop {{ ansible_managed }} — file is rsync-raw
boilerplates/traefik/dynamic/ files are rsynced verbatim to lightning-lane,
not rendered through Jinja first. Traefik also processes Go-style
{{ }} templates, so a leftover {{ ansible_managed }} comment line caused
Traefik to reject the entire file with:

  ERR /etc/traefik/dynamic/honcho.yml: template: :1: function
  "ansible_managed" not defined

Other dynamic configs in this directory deliberately omit the marker;
matching that convention.
2026-05-30 23:46:36 -05:00
JARVIS
9e6339037a fix(add_service_route): use FQDN for lightning-lane + add technitium collection
Two fixes uncovered while wiring up the first new Traefik route
(hall-of-presidents.local.mk-labs.cloud for honcho):

  * traefik_host changed from 10.1.71.35 -> lightning-lane.local.mk-labs.cloud
    so wed/.ssh/config rules (Host *.local.mk-labs.cloud) match and the
    rsync step uses the right IdentityFile.

  * requirements.yml: pin effectivelywild.technitium_dns >=1.1.0 — the
    playbook depends on it but the collection was not declared. Bare
    checkouts would fail without it being installed manually.
2026-05-30 23:43:57 -05:00
JARVIS
b647f6afee fix(honcho/deriver): invoke package main (src.deriver) not module file
src/deriver/deriver.py is a helpers module with no __main__ guard —
running it directly loads it and exits cleanly in ~3s with no logs,
which is what we were seeing. The actual queue-processor entry point
is src/deriver/__main__.py (asyncio.run(run_deriver())), invoked via
"python -m src.deriver".

Discovered during honcho first-deploy on lincoln: deriver container
crash-looped (in the sense that systemd restarted it constantly) but
the underlying process was actually exiting 0 on completion of the
empty helpers module.
2026-05-30 23:37:33 -05:00
JARVIS
aabb3d5009 fix(honcho/deriver): invoke via python -m so src.* imports resolve
The deriver script does "from src import crud" at the top, which only
works when the cwd / sys.path[0] is /app. Running the script by file
path (python /app/src/deriver/deriver.py) puts /app/src/deriver on
sys.path instead, and the import fails with ModuleNotFoundError.

Switch to python -m src.deriver.deriver and explicitly set
WorkingDir=/app so module resolution is deterministic across Podman
versions.

Discovered during the first deploy of the honcho role on lincoln —
honcho-api was healthy on :8000 but the deriver crash-looped 26 times
in two minutes.
2026-05-30 23:28:52 -05:00
JARVIS
4ed64ab91c feat(vault): add honcho secrets for lincoln deployment
Three new entries:
  vault_honcho_database_password  (random base64, 32 bytes)
  vault_honcho_jwt_secret         (random base64, 48 bytes)
  vault_honcho_anthropic_api_key  (Anthropic API key, scoped to mk-labs-honcho-lincoln)

Consumed by ansible/roles/honcho/defaults/main.yml.
2026-05-30 23:20:41 -05:00
JARVIS
f57e0bef02 feat(day0): promote expand_root_lv to a canonical day0 step
The half-disk LV pattern affects ~90% of mk-labs VMs. Treating the
fix-up as application-specific (as it was in day1_deploy_honcho.yml)
means future deploys would each carry the same boilerplate, and any
day1 author could forget it.

This commit:

  * Adds playbooks/day0_expand_root_lv.yml — standalone day0 step,
    targets {{ target | default("all") }}, honors a per-host
    expand_root_lv_skip opt-out for multi-LV layouts.

  * Adds playbooks/day0_provision.yml — umbrella playbook chaining
    day0_linux_baseline + day0_expand_root_lv, so the operator runs
    ONE command per new VM.

  * Removes expand_root_lv from day1_deploy_honcho.yml — day0 is
    assumed complete before day1 begins (cleaner separation of
    concerns, matches the convention day1_deploy_semaphore already
    follows).

  * Updates the role README to document the lifecycle position and
    the opt-out flag for hosts with multi-LV plans.
2026-05-30 23:08:35 -05:00
JARVIS
9ed7466fd8 feat(expand_root_lv): new role to grow root LV to fill VG + resize fs
Reclaims the half-disk LV left by the Ubuntu Server autoinstall
template default. Idempotent — no-ops cleanly when there are no free PE
in the VG, and exits the play cleanly on hosts without LVM.

Supports ext4 and xfs. Does not handle partition resize (cloud-utils
growpart) — out of scope for the template fix-up case.

Wired into day1_deploy_honcho.yml ahead of the honcho role so newly
provisioned VMs get the fix-up automatically. Suitable to add to any
day1 playbook by simply listing it before the application role.
2026-05-30 23:03:10 -05:00
JARVIS
4d7766d1b1 feat(honcho): add role + day1 playbook + traefik route for lincoln
Deploys Honcho (plastic-labs/honcho) as a rootful Podman + Quadlet
service on the lincoln VM (10.1.71.132). Three containers on a
user-defined network:

  - honcho-postgres  pgvector/pgvector:pg16
  - honcho-api       FastAPI on :8000
  - honcho-deriver   background worker for theory-of-mind derivations

LLM provider: Anthropic Claude (claude-sonnet-4-5). Switching providers
is two env-var changes — see README.

Traefik route hall-of-presidents.local.mk-labs.cloud -> lincoln:8000
added under boilerplates/traefik/dynamic/. JARVIS itself talks to
Honcho directly at lincoln:8000 (east-west); the Traefik alias exists
only for browser access to the Swagger /docs UI.

Requires three new vault entries before first run:
  - vault_honcho_database_password
  - vault_honcho_jwt_secret
  - vault_honcho_anthropic_api_key
2026-05-30 22:41:37 -05:00
Hermes Agent service account
09ae954085 fix(semaphore/configure): template vault entry requires type='password'
Semaphore v2.18 source (db/TemplateVault.go) shows FillTemplateVault
only loads the key when type==TemplateVaultPassword. With type left
as the default empty string, the vault key is stored but never
materialised at task-run time; ansible-playbook gets no password and
fails with 'Attempting to decrypt but no vault secrets found'.

Confirmed by patching live templates via PUT with type='password';
next task run succeeded.
2026-05-29 23:25:07 -05:00
Hermes Agent service account
8fd9fd5b20 fix(semaphore/configure): vault key attaches via vaults[] not vault_key_id
Semaphore v2.18 replaced the single vault_key_id field with a vaults[]
array supporting multi-vault per template. Sending vault_key_id is
silently ignored — template stores no vault association, runs fail
at the playbook stage with 'Attempting to decrypt but no vault
secrets found'.

Wrap the single configured vault password in the required envelope:
  vaults: [{id: 0, vault_key_id: <key_id>, name: 'default'}]

Empirically verified: PUT with this shape returns 204 and the GET
echoes the vault back with a server-assigned id.
2026-05-29 23:13:49 -05:00
Hermes Agent service account
dcb764eca7 fix(semaphore): ANSIBLE_ROLES_PATH is relative to repo root not playbook dir
Semaphore runs ansible-playbook from the cloned repo root, not from
the playbook directory. The previous value '../roles' resolved
outside the repo. Correct path is 'ansible/roles'.
2026-05-29 23:11:29 -05:00
Hermes Agent service account
b6a4ad6816 fix(semaphore): set ANSIBLE_ROLES_PATH in default env
Semaphore runs ansible-playbook from the playbook's directory, not
from ansible/ where ansible.cfg lives. Roles aren't found at runtime:
'role linux-baseline was not found'.

Set ANSIBLE_ROLES_PATH=../roles in the default environment so the
search picks up ansible/roles/ relative to the playbook directory.
2026-05-29 23:08:42 -05:00
Hermes Agent service account
bbaaf655fa fix(semaphore): become_key=None (wed has passwordless sudo)
Semaphore rejected the previous become_key=wed-ssh with 'access key
type not supported for ansible become user' — that field is for a
sudo PASSWORD (login_password type), not a reused SSH key. wed has
passwordless sudo on every host (set up by the VM template), so the
correct value is the built-in 'None' key.
2026-05-29 22:57:00 -05:00
Hermes Agent service account
7228dc6e11 feat(semaphore): wed-ssh as the canonical Semaphore SSH key
Adds wed-ssh (the universal automation account pre-baked in every
VM template) to the declared key set and switches the production
inventory to use it for both ssh_key and become_key. Retains
jarvis-ssh for cases that specifically need admin-level access.

This aligns Semaphore-driven jobs with the established homelab
convention: wed runs the playbooks, jarvis is the higher-privilege
admin account provisioned by linux-baseline.

Operator (Ryan) cleaned out the previous templates + inventory in
Semaphore before this commit so the configure step re-creates them
wired to wed-ssh on its next run.
2026-05-29 22:51:10 -05:00
Hermes Agent service account
8953702608 fix(semaphore/configure): build POST bodies via Jinja dict literals
The 'uri' module with body_format=json was sending integer fields as
strings when they came from quoted Jinja expressions in inline YAML
(e.g. ssh_key_id: "{{ ... | int }}"), because YAML loads the quoted
form as a string and Ansible doesn't coerce back. Semaphore rejects
that with HTTP 400.

Build each body as a Jinja dict literal in a folded scalar so types
survive: integers stay integers, strings stay strings.

Also restores no_log: true on key creation now that we're past the
debug round.
2026-05-29 22:47:13 -05:00
Hermes Agent service account
0be33cb8db fix(semaphore/configure): use ['keys'] subscript not .keys attribute
Jinja interpreted semaphore_config.keys as the dict method rather than
the 'keys' field, causing 'Invalid data passed to loop' failure. Bracket
subscript disambiguates.
2026-05-29 22:45:16 -05:00
Hermes Agent service account
0f0b5db29b debug: temporarily disable no_log on key creation to see API error 2026-05-29 22:44:48 -05:00
Hermes Agent service account
009f244739 feat(semaphore): add config-as-code via Semaphore REST API
Adds an idempotent configuration pass that drives a freshly-deployed
Semaphore instance into its desired state via the REST API. Declared
in group_vars/all/semaphore.yml, applied by tasks/configure.yml,
toggled by semaphore_configure feature flag (default off).

Object types managed:
  - Project (mk-labs)
  - Keys (ansible-vault-pass, gitea-deploy, jarvis-ssh)
  - Repositories (homelab on gitea)
  - Inventories (production -> ansible/inventory.yml in homelab repo)
  - Environments (default with ANSIBLE_HOST_KEY_CHECKING=False)
  - Templates (day0_linux_baseline + variants, day1_deploy_semaphore)
    with survey vars for runtime parameters

Each object found-or-created by name; existing ones never modified.
no_log on token-bearing calls to keep secrets out of stdout.

Inputs (already in vault):
  vault_semaphore_api_token
  vault_jarvis_ssh_private_key
  vault_gitea_deploy_key
  vault_ansible_vault_password
2026-05-29 22:44:15 -05:00
Hermes Agent service account
2f87039f17 vault: add jarvis SSH key, gitea deploy key, and Semaphore API token
Three new vault entries required for Semaphore config-as-code:
  - vault_jarvis_ssh_private_key (matches jarvis_ssh_public_key in
    group_vars/all/vars; used by Semaphore to SSH to the fleet)
  - vault_gitea_deploy_key (existing deploy key on the homelab
    repo; used by Semaphore to clone)
  - vault_semaphore_api_token (minted from the Semaphore UI; used
    by Ansible to drive Semaphore configuration)

These wire up the inputs the upcoming semaphore configure step
will consume.
2026-05-29 22:37:34 -05:00
Hermes Agent service account
72fa38e928 fix(semaphore): force container restart when Quadlet template changes
Quadlet regenerates the systemd unit on daemon-reload but does not
restart the running container — the process keeps its original
environment until restarted. Add explicit 'state: restarted' tasks
gated on the Quadlet template's changed status so env-var updates
actually take effect on re-runs.
2026-05-29 21:53:47 -05:00
Hermes Agent service account
9e68802090 fix(semaphore): make host-agnostic by omitting SEMAPHORE_WEB_ROOT
The SPA was rendering <base href="https://semaphore.local.mk-labs.cloud/">
regardless of which hostname served the page, causing the imagineering
alias to load the same UI but rewrite all in-page navigation back to the
semaphore hostname. Confusing for users hitting either Traefik alias.

Leave SEMAPHORE_WEB_ROOT empty so Semaphore emits relative URLs and is
fully host-agnostic. Both DNS names (semaphore + imagineering) now serve
cleanly without preference between them.

The template only emits SEMAPHORE_WEB_ROOT when the variable is set, so
the env-var is absent from the container when not needed.
2026-05-29 21:52:33 -05:00
Hermes Agent service account
d05cfcf317 fix(semaphore): provide SEMAPHORE_ADMIN_* env vars for non-interactive first boot
The v2.18 image's entrypoint runs the setup wizard on first boot. Without
the SEMAPHORE_ADMIN_* variables it prompts on stdin, fails with 'Username
cannot be empty', and the container exits — leading to a crash loop.

Set:
  SEMAPHORE_ADMIN=admin
  SEMAPHORE_ADMIN_NAME=Administrator
  SEMAPHORE_ADMIN_EMAIL=admin@local.mk-labs.cloud
  SEMAPHORE_ADMIN_PASSWORD={{ vault_semaphore_admin_password }}
  SEMAPHORE_PLAYBOOK_PATH=/var/lib/semaphore/playbooks

The env-var bootstrap path is stable in v2.x; only the legacy
'semaphore user add' CLI invocation was unreliable. Drop the manual
user-add step from the README.
2026-05-29 21:38:44 -05:00
Hermes Agent service account
80f810fb0c feat(semaphore): rewrite role with rootful Podman Quadlet + PostgreSQL
Complete rewrite of the semaphore role. Supersedes three prior
iterations whose admin-user-creation logic was unreliable across
Semaphore CLI versions.

Architecture:
  - Rootful Podman Quadlet under /etc/containers/systemd/
  - Separate PostgreSQL 16-alpine container on a user-defined
    podman network (semaphore-net)
  - Named volumes for both data stores (semaphore_data,
    semaphore_postgres_data) so container recreation is
    non-destructive
  - Pinned image tags: semaphoreui/semaphore:v2.18.5-ansible2.16.5
    and postgres:16-alpine
  - Post-deploy HTTP health check fails the playbook if Semaphore
    doesn't respond on /api/ping within ~60s

Admin user creation remains intentionally manual after first deploy;
the role README documents the exact podman exec command.

Removes the duplicate deploy_semaphore.yml and the now-unneeded
cleanup_semaphore.yml; day1_deploy_semaphore.yml is the canonical
entry point.
2026-05-29 21:34:22 -05:00
Hermes Agent service account
9153324795 fix(linux-baseline): correct MOTD padding math
Previous template used inline arithmetic that miscounted the box
width, leaving the right border misaligned on real hostnames. Switch
to computed labels + a fixed inner_width so any hostname / OS string
pads to the same border position.
2026-05-29 20:56:38 -05:00
Hermes Agent service account
91b5817e5f feat(ansible): add linux-baseline role and day0_linux_baseline playbook
Introduces a single, idempotent baseline role to supersede the
overlapping day0-baseline and common roles. Capabilities are
feature-flagged so they can be toggled per-host:

  - packages (common + OS-family + per-host extras)
  - timezone + locale
  - chrony time sync against sundial
  - baseline users (jarvis admin account with SSH key + NOPASSWD sudo)
  - SSH hardening via /etc/ssh/sshd_config.d/ drop-in
  - unattended security upgrades (Debian family)
  - sysctl drop-in at /etc/sysctl.d/99-mk-labs.conf
  - journald retention caps
  - branded MOTD

Ubuntu/Debian is first-class; vars/RedHat.yml provides a placeholder
for future distros via the ansible_os_family pattern.

The legacy day0-baseline and common roles remain in place for now and
will be removed during the playbook cleanup sweep, alongside the
existing playbook naming inconsistencies.
2026-05-29 20:40:04 -05:00
Hermes Agent service account
1dfa7889ab chore(playbooks): switch day0_baseline.yml to new day0-baseline role 2026-05-28 22:06:49 -05:00
Hermes Agent service account
08d7da0c35 feat(baseline): add clean day0 baseline role (time, packages, hardening) 2026-05-28 21:54:34 -05:00
Hermes Agent service account
9ebeb42023 fix(semaphore): add SEMAPHORE_DB_SSLMODE=disable for postgres connection 2026-05-28 21:24:14 -05:00
Hermes Agent service account
075f34b1fb fix(semaphore): make network creation task more reliable 2026-05-28 15:22:45 -05:00
Hermes Agent service account
210c89c2c7 fix(semaphore): make network creation more robust and earlier in legacy block 2026-05-28 15:20:09 -05:00
Hermes Agent service account
b93a6e50ab feat(semaphore): add optional aggressive storage cleanup for legacy mode 2026-05-28 15:17:54 -05:00
Hermes Agent service account
85cc1f8c6a fix(semaphore): improve container cleanup and volume reuse in legacy mode 2026-05-28 15:16:07 -05:00
Hermes Agent service account
8e781b0c54 fix(semaphore): add container cleanup at start of legacy postgres deployment 2026-05-28 15:14:34 -05:00
Hermes Agent service account
d8ad35b8e9 fix(semaphore): add dedicated network for legacy postgres deployment 2026-05-28 15:13:16 -05:00
Hermes Agent service account
a9973d1e0f feat(semaphore): add legacy postgres deployment path using podman_container 2026-05-28 15:07:47 -05:00
4113011f63 fixed semaphore template 2026-05-28 14:51:04 -05:00
Hermes Agent service account
018782d986 fix(semaphore): remove User/Group from quadlet template 2026-05-28 14:49:37 -05:00
Hermes Agent service account
3681e8c03e fix(semaphore): make volume creation task more robust 2026-05-28 14:31:06 -05:00
Hermes Agent service account
8f377e4cf3 fix(semaphore): create named volumes before deploying quadlets 2026-05-28 14:25:18 -05:00
Hermes Agent service account
419acaa40d fix(semaphore): use systemctl start for quadlet services after daemon-reload 2026-05-28 14:08:53 -05:00
Hermes Agent service account
42b204bf8a fix(semaphore): make quadlet deployment self-contained with immediate daemon-reload + service start 2026-05-28 14:06:24 -05:00
Hermes Agent service account
1d7dcb7d82 fix(semaphore): load role defaults in cleanup playbook so variables are defined 2026-05-28 14:02:33 -05:00
Hermes Agent service account
d63ca0b4f9 docs(semaphore): add cleanup playbook reference to deploy playbook 2026-05-28 14:01:00 -05:00
Hermes Agent service account
e9440327aa feat(semaphore): add dedicated cleanup playbook
- Explicit playbook for removing old container, systemd services, and quadlets
- Optional semaphore_force_clean variable for data removal
- Safer than tags for destructive operations
2026-05-28 10:25:56 -05:00
Hermes Agent service account
dcc7e282c7 feat(semaphore): complete quadlet deployment for PostgreSQL + Semaphore
- Add quadlet tasks and handlers for modern rootless Podman deployment
- Fix broken Jinja in semaphore.container.j2
- Add proper 0777 permissions handling for rootless
- Support semaphore_use_postgres toggle with fallback to legacy BoltDB path
2026-05-28 10:22:28 -05:00
Hermes Agent service account
6d5fc7c5c6 fix(semaphore): set data directory permissions to 0777 for rootless compatibility 2026-05-27 22:38:36 -05:00
312fdf9986 fix env.j2 2026-05-27 22:26:51 -05:00
Hermes Agent service account
4e0b4fa049 refactor(semaphore): remove unreliable admin user creation from role
Initial admin user must now be created manually after first deployment
2026-05-27 22:20:43 -05:00
Hermes Agent service account
cf7c2a1436 fix(semaphore): clean up admin user creation tasks with proper fallback and force logic 2026-05-27 22:16:34 -05:00
Hermes Agent service account
3dc6555ad1 fix(semaphore): support vault_semaphore_admin_password as fallback variable name 2026-05-27 22:15:20 -05:00
Hermes Agent service account
27ff9286b4 feat(semaphore): add semaphore_force_admin_user option
- When set to true, deletes existing admin user before creating
- Useful for recovering from bad password or broken user state
2026-05-27 22:10:32 -05:00
Hermes Agent service account
6e50461999 docs: add comprehensive Semaphore deployment and setup guide
- Covers deployment, initial setup, DNS/Traefik, GitOps approach, and troubleshooting
2026-05-27 21:57:02 -05:00
Hermes Agent service account
3f1c3a40cf feat(semaphore): add idempotent initial admin user creation
- New variables for admin user (password from vault)
- Task checks if user exists before creating
- Uses podman exec + semaphore CLI
2026-05-27 21:56:36 -05:00
Hermes Agent service account
0116ec4cc3 fix(dns): improve hostname extraction in add_service_route.yml
- Use explicit Jinja2 loop for more reliable parsing of Host() rules
- Handles multi-host router definitions correctly
2026-05-27 21:39:51 -05:00
Hermes Agent service account
f962d0a6d7 feat(dns): rewrite add_service_route.yml to support multi-host Traefik configs
- Parse Host() rules from router definitions
- Supports multiple hostnames per service file (e.g. semaphore + imagineering)
- More robust and future-proof
2026-05-27 21:31:41 -05:00
Hermes Agent service account
dc3c0d7cb1 feat(traefik): add semaphore dynamic config with dual hostnames
- semaphore.local.mk-labs.cloud
- imagineering.local.mk-labs.cloud
- Backend: figment:3000
- Cloudflare certResolver + security-headers
2026-05-27 21:24:02 -05:00
Hermes Agent service account
b05f9fad09 fix(semaphore): add :U volume flag for proper rootless UID mapping
Prevents permission issues with BoltDB data directory in rootless Podman
2026-05-27 21:17:40 -05:00
Hermes Agent service account
8650995926 fix(semaphore): adjust BoltDB Path in config.json to parent directory
Prevents 'database.boltdb/database.boltdb' path issue in v2.18.x
2026-05-27 21:09:12 -05:00
Hermes Agent service account
e5469d2cb8 fix(semaphore): use semaphore_user for directory ownership + add config.json for BoltDB
- Replace hardcoded UID 1000 with {{ semaphore_user }} / {{ semaphore_group }}
- Add config.json creation task for non-interactive BoltDB startup
- Prevents interactive setup wizard and permission errors on v2.18.x
2026-05-27 21:07:30 -05:00
Hermes Agent service account
702698ddcd fix(semaphore): always regenerate systemd unit and use DB_DIALECT
- Switch podman_container env to SEMAPHORE_DB_DIALECT
- Remove creates: guard on podman generate systemd task
- Add changed_when so unit is always updated on role run
2026-05-27 20:56:47 -05:00
Hermes Agent service account
d233d582d4 fix(semaphore): use SEMAPHORE_DB_DIALECT instead of legacy DB_TYPE
- Updated container quadlet template to use modern SEMAPHORE_DB_DIALECT variable
- Updated env.j2 template for consistency with current SemaphoreUI expectations
- Resolves BoltDB not being honoured on v2.18.x
2026-05-27 20:48:41 -05:00
Hermes Agent service account
3839fac162 fix: run directory ownership task with become: true 2026-05-26 23:04:23 -05:00
Hermes Agent service account
b01dac85da fix: set correct ownership (1000:1000) on semaphore data directories 2026-05-26 23:02:04 -05:00
Hermes Agent service account
37a49824d0 fix: run podman generate systemd as root 2026-05-26 22:57:53 -05:00
Hermes Agent service account
0127016ab2 fix: run Semaphore container as root inside container 2026-05-26 22:57:10 -05:00
Hermes Agent service account
e0b6fcb24a fix: use 'latest' as default Semaphore image tag 2026-05-26 22:55:12 -05:00
Hermes Agent service account
e7d9a8fec5 fix: use containers.podman collection instead of community.general 2026-05-26 22:53:57 -05:00
Hermes Agent service account
1a9addc537 feat: use community.general collection + requirements.yml for semaphore 2026-05-26 22:51:59 -05:00
Hermes Agent service account
401f25b1c4 fix: use raw podman commands (no external collections required) 2026-05-26 22:50:55 -05:00
Hermes Agent service account
ece522074e fix: switch semaphore role to podman_container + generate systemd (more reliable) 2026-05-26 22:50:18 -05:00
Hermes Agent service account
c30c0074f1 fix: separate daemon-reload from service start in semaphore role 2026-05-26 22:49:38 -05:00
Hermes Agent service account
fa51dc2c4d fix: correct Quadlet service startup in semaphore role 2026-05-26 22:48:55 -05:00
Hermes Agent service account
c1810fde8a fix: replace semaphore role with new Podman + Quadlet version 2026-05-26 22:46:46 -05:00
a781ef8b14 update inventory 2026-05-26 22:44:48 -05:00
Hermes Agent service account
92b2a9d609 refactor: consolidate all roles into ansible/roles/ and update ansible.cfg
- Move all roles from playbooks/roles/ to roles/
- Update roles_path in ansible.cfg
- Add cast user to common role
- Create standalone podman role
- Add semaphore role with Podman + Quadlet support
2026-05-26 22:22:08 -05:00
Hermes Agent service account
9250b0f193 chore: remove defunct hermes directory
The previous Hermes deployment configuration has been superseded.
This directory is no longer used and is being removed as part of
the transition to the new command centre on carousel-of-progress.
2026-05-26 14:42:45 -05:00
24869f47ee deply hermes VM 2026-05-26 11:48:23 -05:00
bb5a57e909 remove jarvis deployment 2026-05-25 21:16:52 -05:00
9f3d81729d fix(jarvis): run as root for NFS compat, add namespace PSA baseline label 2026-05-25 20:43:03 -05:00
064d3e8b3d fix(jarvis): move runAsNonRoot to container level, allow init container to run as root for chown 2026-05-25 20:39:39 -05:00
ef7e3c61ed fix(jarvis): add init container to fix PVC ownership for UID 10000 2026-05-25 20:37:01 -05:00
64951e1e5e fix(jarvis): use args not command for hermes entrypoint 2026-05-25 20:35:32 -05:00
58931732f7 fix(jarvis): correct hermes entrypoint command 2026-05-25 20:31:46 -05:00
be8e50d590 deploy hermes 2026-05-25 20:21:24 -05:00
e309acd67d fix typo 2026-05-24 21:15:08 -05:00
4e7f14ea5a additional service name changes 2026-05-24 21:12:10 -05:00
b79f0505b7 update pbs to timekeeper 2026-05-24 19:46:19 -05:00
045ac85353 update glance configmap 2026-05-24 19:43:13 -05:00
60af4304b6 fix url 2026-05-24 18:35:52 -05:00
96e946ac09 change to https 2026-05-24 18:33:16 -05:00
6584ed9dc4 added glance dashboard 2026-05-24 17:04:49 -05:00
3017c27910 fix port 2026-05-19 23:03:49 -05:00
c25c2a25ad update referencegrant 2026-05-19 22:41:41 -05:00
0476e489d4 more 2026-05-19 22:35:05 -05:00
bf0d7a20a7 update gateway 2026-05-19 22:31:22 -05:00
ed0091cd05 adjust session duration 2026-05-19 22:25:00 -05:00
402f93ab38 add ingnore 2026-05-19 22:16:31 -05:00
8944da8c99 folder rename 2026-05-19 22:06:48 -05:00
d671700466 deploy headlamp 2026-05-19 22:00:34 -05:00
562e102a9b enable metrics 2026-05-19 21:34:46 -05:00
f4c60d7560 add grafana dashboards 2026-05-19 21:12:54 -05:00
0dae8327eb update prometheus scrape 2026-05-18 22:57:07 -05:00
39b7b91f45 update snmp exporter 2026-05-18 22:32:13 -05:00
a7f77514d6 additional monitoring scrapes 2026-05-18 22:28:14 -05:00
b45dcea4c3 update gateway spec 2026-05-18 22:01:18 -05:00
2ed70b4964 Deploy monitoring stack 2026-05-18 21:42:32 -05:00
dbf0b18f07 authentik for argo 2026-05-18 19:43:21 -05:00
1cfc9b5f5d align gateway spec 2026-05-18 19:28:50 -05:00
c8f75bef8a fix referencegrant 2026-05-18 19:25:41 -05:00
fc171b48e9 deploy gateway 2026-05-18 19:11:42 -05:00
5504d0c6f4 add gateway to external dns 2026-05-18 18:15:33 -05:00
025e9a8f1c Added bootstrap folder and gateway controller 2026-05-18 18:07:35 -05:00
06db61c6c0 enable nfs csi 2026-05-18 17:19:39 -05:00
8601ad9cd9 move archive 2026-05-18 15:15:36 -05:00
1f7318f1c3 repo cleanup 2026-05-18 15:13:12 -05:00
cdc78955b1 cilium, argo 2026-05-18 13:20:42 -05:00
6f0c751f63 fix: set enforce:privileged for Cilium compatibility 2026-05-18 10:43:27 -05:00
f1ef759206 fix(cilium): update LB pool API from v2alpha1 to v2 2026-05-18 00:59:17 -05:00
5f5f8cb173 fix argocd 2026-05-18 00:45:47 -05:00
eb93eb7352 fix(argocd): remove conflicting server patch, params-cm handles insecure mode 2026-05-18 00:39:43 -05:00
9cd9a30fbe fix(argocd): switch to ingress-nginx, fix shared resource warning, add insecure mode 2026-05-18 00:34:21 -05:00
5a30521d66 fix(argocd): use env var for insecure mode instead of args 2026-05-18 00:23:03 -05:00
725d403d34 helm fix 2026-05-18 00:08:30 -05:00
9a604042ca update cilium 2026-05-18 00:01:03 -05:00
10406e1705 remove gateway api 2026-05-17 23:48:22 -05:00
4717f63bc3 cilium 2026-05-17 23:46:44 -05:00
2ff74cdc7c cilium TS 2026-05-17 23:39:11 -05:00
87f2d6bf95 fix cilium 2026-05-17 23:32:24 -05:00
88ec796267 ext dns and cert manager 2026-05-17 22:59:45 -05:00
344a29a374 fix directory for 1p 2026-05-17 22:55:32 -05:00
c7fbe8e963 fix(platform): move gateway and namespace into manifests/ directory 2026-05-17 22:48:51 -05:00
03acab784a fix(argocd): use args not command for --insecure flag 2026-05-17 22:24:26 -05:00
3ce3ecac66 feat(platform): vendor gateway-api CRDs v1.2.1 2026-05-17 22:19:04 -05:00
bc24c00c50 final apps 2026-05-17 21:41:28 -05:00
3e9a843e1b cert manager and 1p 2026-05-17 21:37:17 -05:00
5e99408c1f change pattern for external secrets 2026-05-17 21:33:24 -05:00
e4a2c47da2 fix(platform): correct helm valueFiles field name for ArgoCD 3.x 2026-05-17 21:18:47 -05:00
13ebe67a35 update 2026-05-17 21:10:45 -05:00
ca1cdd0634 fix(argocd): restore SSH repo credentials for Gitea 2026-05-17 21:10:10 -05:00
74e53d1364 fix(platform): valuesFile → valuesFiles for ArgoCD 3.x compatibility 2026-05-17 21:04:21 -05:00
e946bd71f0 feat(platform): vendor gateway-api CRDs v1.2.1 2026-05-17 20:45:53 -05:00
905b4619d6 Initial argo deployment 2026-05-17 20:44:31 -05:00
97e9889251 feat(fastpass): Talos cluster provisioning and bootstrap
- Terraform: VM provisioning, Unifi DHCP, Technitium DNS
- talhelper: cluster config for 6-node Talos cluster
- Cilium 1.19.4 CNI with Talos-compatible security context
- docs: city-hall setup guide and bootstrap runbook
2026-05-17 16:08:19 -05:00
9f3ac95d8d gitea and authentik 2026-05-16 16:22:14 -05:00
84523d0054 configure prometheus for ubiquiti and proxmox cluster 2026-05-08 00:25:55 -05:00
f8c6b327f9 Deploy gites 2026-05-07 22:07:53 -05:00
79662aa545 Deployed prometheus/grafana 2026-04-26 23:00:37 -05:00
69b91a2e1d Move nextcloud external access to traefik. 2026-04-18 21:51:28 -05:00
9961fe1ed4 deploy nextcloud 2026-04-18 00:36:40 -05:00
4db6f0b06f added test playbook 2026-03-22 23:47:47 -05:00
307413f3f2 Ansible deployed via boilerplates and playbooks 2026-03-22 18:15:55 -05:00
836ef66cf0 fix dash 2026-03-21 19:48:17 -05:00
4cbaebc98b rename playbook 2026-03-21 19:46:59 -05:00
c1d85b7f89 Remove NetBox Traefik config - deferred until Compose rebuild 2026-03-21 19:10:10 -05:00
c087f32355 redeploy authentic policies as code. 2026-03-21 14:00:37 -05:00
9ad585681f fix ssh handler 2026-03-19 16:53:45 -05:00
40ab77f061 update test file 2026-03-19 16:44:59 -05:00
1d0adb7689 step enrollment 2026-03-19 16:29:09 -05:00
1d6b216b74 remove step-ca from traefik 2026-03-14 22:39:35 -05:00
df0d81ec4d fix directory permissions 2026-03-14 22:32:40 -05:00
ea7d079f27 update step playbook 2026-03-14 22:30:18 -05:00
5022fc90ab update ansible inventory 2026-03-14 22:27:20 -05:00
45c478bede fixed terraform 2026-03-14 22:21:22 -05:00
03b8abbe4d fix terraform 2026-03-14 22:18:28 -05:00
42ea1e2d03 deploy step 2026-03-14 22:15:28 -05:00
b85b237129 rename turnstile to guest-relations 2026-03-14 21:08:35 -05:00
1d6a8c154c updated documentation 2026-03-10 20:41:34 -05:00
6f14d48d5e oidc again 2026-03-10 20:34:00 -05:00
fa9fd9c65f fix oidc 2026-03-10 20:30:07 -05:00
86cf979453 debugging 2026-03-10 20:28:06 -05:00
cc0fd647ab update ansible user for proxmox 2026-03-10 20:21:53 -05:00
591522f643 updated ansible inventory (proxmox) 2026-03-09 22:41:13 -05:00
643fefb4bf ansible for proxmox config 2026-03-09 22:36:42 -05:00
e8810195cd updwated documentation 2026-03-09 16:27:30 -05:00
3d2669497a update traefik config 2026-03-08 20:39:34 -05:00
42f7a2773d updated inventory 2026-03-08 20:34:59 -05:00
35c1bdb3bf ansible for authentik deployment. 2026-03-08 20:30:51 -05:00
9605b9d3bd Added authentic boilerplate 2026-03-08 16:23:11 -05:00
dfa219191d add terraform definition for turnstile 2026-03-08 16:12:16 -05:00
14502df261 update tradfik networking 2026-03-08 15:54:15 -05:00
ea7e05dcc2 fixed docker install 2026-03-08 15:47:18 -05:00
26f7d3214f switched ntp client to chrony 2026-03-08 15:41:09 -05:00
173a36c518 changed hosts 2026-03-08 15:31:23 -05:00
fcb1777336 move roles 2026-03-08 15:28:09 -05:00
f82c13cd09 updated ansible 2026-03-07 23:08:06 -06:00
e9a854faed moved boilerplates 2026-03-07 22:49:59 -06:00
344e8dff69 updated ansible for lightning-lane 2026-03-07 22:47:37 -06:00
36c2e9540c updated tfvars 2026-03-07 22:00:41 -06:00
40dda3241f update vm initialization 2026-03-07 21:56:49 -06:00
db67f71a3c added variables 2026-03-07 21:53:56 -06:00
920ea80261 terraform files in the right folder 2026-03-07 21:46:44 -06:00
ad03943339 added tfvars 2026-03-07 21:25:54 -06:00
a349ce13f8 Added terraform for lightning-lane 2026-03-07 21:18:36 -06:00
7d7129632f updated technical tdp 2026-03-07 17:50:08 -06:00
e3b3401544 VM provisioning idempotency 2026-03-06 23:10:56 -06:00
bba9278895 n8n workflow to create a VM from Netbox 2026-02-28 20:06:41 -06:00
d941557e88 Update terraform to use vm_id from n8n. 2026-02-28 00:00:56 -06:00
4964552483 fix(terraform): set OVMF bios, correct DNS domain, remove VLAN from interface 2026-02-27 19:56:15 -06:00
7e0b0a859b feat(terraform): add Proxmox VM and Unifi DHCP modules (Phase 2) 2026-02-27 19:12:25 -06:00
b33bd5f252 feat(netbox): add custom field, VLAN, and prefix initializers (Phase 1.1-1.4) 2026-02-25 21:43:47 -06:00
63b9a8fd13 docs: rewrite README for mk-labs architecture 2026-02-25 20:55:38 -06:00
6350cb681f chore: update .gitignore for new repo structure (fix terraform/ exclusion) 2026-02-25 20:53:01 -06:00
676fc02c91 feat: create pipeline directory structure (terraform, n8n, netbox, docs) 2026-02-25 20:51:26 -06:00
75e0d99495 refactor: move Packer templates to top-level packer/ directory 2026-02-25 20:51:20 -06:00
b51cd1a868 refactor: move ansible.cfg to ansible/ directory 2026-02-25 20:51:12 -06:00
7187dc8280 refactor: move shared Ansible tasks to ansible/tasks/ 2026-02-25 20:51:02 -06:00
86708542c9 refactor: move Ansible roles to standard ansible/roles/ location 2026-02-25 20:46:33 -06:00
74172c4e5a chore: archive deprecated infra-config content (SNO cluster, duplicate ansible, old playbooks) 2026-02-25 20:45:01 -06:00
8c8835d1d5 chore: archive deprecated Ansible content (OpenShift, Fastpass, FreeIPA, k8s) 2026-02-25 20:44:49 -06:00
286d20f8c1 chore: archive OpenShift/ACM cluster content, hub-bootstrap, and applications 2026-02-25 20:44:37 -06:00
05ba1bbdb3 chore: remove misc files 2026-02-25 20:43:28 -06:00
7c2ef04d47 Sync before Archive 2026-02-25 20:36:50 -06:00
73eee46101 ansible vm creation (depreciated) 2025-11-21 05:48:43 -08:00
510da9c2a4 Sync 11-19 2025-11-19 09:36:59 -08:00
702c71fcff sync 2025-10-19 17:02:16 -05:00
3f31f77bc8 moving 2025-09-30 11:23:27 -05:00
6f5bc65171 ansible scripts done to install kubewrnetes prequesites 2025-09-27 16:05:33 -05:00
6107e474c1 sync all 2025-09-26 11:55:32 -05:00
e7f8fd2ccd fix logic 2025-09-23 18:26:13 -05:00
1272fa3cb3 reapply password 2025-09-23 16:52:02 -05:00
a76c99e825 .. 2025-09-23 15:30:38 -05:00
d1b6df7d94 typo 2025-09-23 15:29:12 -05:00
14d28f7937 re-enable cloud-init 2025-09-23 15:27:53 -05:00
a67818c0f3 ssh 2025-09-22 22:08:39 -05:00
0d03a42337 Merge branch 'main' of https://github.com/rblundon/homelab 2025-09-22 20:10:24 -05:00
853cd90148 upsate ssh 2025-09-22 20:10:22 -05:00
4108600a57 disable cloud-init on guest 2025-09-22 15:26:31 -05:00
59826cb7a9 update key to variable 2025-09-22 10:40:52 -05:00
cb99f1f5d2 added key 2025-09-22 10:29:33 -05:00
a61491acf3 fix path 2025-09-22 10:26:25 -05:00
7b483364be trying with ssh key 2025-09-22 10:25:20 -05:00
9a8978d046 disable swap 2025-09-22 09:42:35 -05:00
72b943ee04 update password 2025-09-22 09:36:13 -05:00
dd3cf2cd0f revert user file 2025-09-21 18:42:01 -05:00
dbd1c94ada remove packages 2025-09-21 18:18:37 -05:00
c532e7b7de add machine type 2025-09-21 18:00:31 -05:00
3c90b68896 packer bug resolved 2025-09-21 17:27:24 -05:00
36c5215ed4 :( 2025-09-21 15:03:45 -05:00
d7d0027b64 new user-data file 2025-09-20 21:24:07 -05:00
428809719a enable reboot 2025-09-19 11:46:44 -05:00
45d8c51a75 remove package 2025-09-19 11:25:23 -05:00
7c605a7337 get rid of swap 2025-09-19 10:14:32 -05:00
a3cd499297 fix auto 2025-09-19 09:44:43 -05:00
ac0a4c0580 increase timeout 2025-09-19 09:24:02 -05:00
932beed25a more TS 2025-09-19 08:50:30 -05:00
79f5a9d129 more CI 2025-09-18 22:26:52 -05:00
b2ebe28397 CI 2025-09-18 22:16:45 -05:00
6c300ca545 increase ssh timeout 2025-09-18 21:16:24 -05:00
eea499a32c fix 2025-09-18 09:36:40 -05:00
fcd839a00e set http port 2025-09-18 09:35:20 -05:00
45b5e65e36 fix boot command 2025-09-18 09:32:07 -05:00
900098b9c1 fix 2025-09-18 09:30:17 -05:00
1b5213af1d http port 2025-09-18 09:28:25 -05:00
efcd022d27 troubleshoot cloud init 2025-09-18 09:03:37 -05:00
9f328a01a4 update user-data 2025-09-17 23:28:44 -05:00
97e9c8c037 fixed build issue 2025-09-17 23:21:42 -05:00
b885d1d707 fix ISO 2025-09-17 23:16:50 -05:00
a084340ce0 fix filename 2025-09-17 23:04:49 -05:00
1b5597ac09 fix filename 2025-09-17 23:00:27 -05:00
8710665255 updated packer files 2025-09-17 22:48:49 -05:00
81e292dc2b typo 2025-09-16 22:16:43 -05:00
aa5087882d tweak 2025-09-16 22:09:00 -05:00
1959a667d4 create ubuntu small packer config 2025-09-16 21:55:49 -05:00
405aae5209 sync current state 2025-09-15 22:27:42 -05:00
b7e090ed7c Merge branch 'main' of https://github.com/rblundon/homelab 2025-08-20 09:55:43 -05:00
02b788ee63 ansible kubernetes deployment 2025-08-20 09:52:28 -05:00
a642d2382d start ansible scripts 2025-08-19 17:06:29 -05:00
6ea3611e08 sync 2025-08-18 12:21:05 -05:00
a255908108 fix name 2025-08-18 09:09:43 -05:00
9b85eabf41 additional vm template changes and k8s node definitions 2025-08-18 09:07:03 -05:00
1fb37b2da9 added new template for large-plus 2025-08-17 22:48:39 -05:00
4193f33183 updated packer templates for new proxmox cluster 2025-08-16 21:50:04 -05:00
2ae45aca00 wrong cluster 2025-08-14 22:40:25 -05:00
19f7af6b1c fix issuer 2025-08-14 22:30:26 -05:00
94f609140b change ceert 2025-08-14 22:26:25 -05:00
d60f24183b change ingress 2025-08-14 22:18:00 -05:00
1728ede010 Merge branch 'main' of https://github.com/rblundon/homelab 2025-08-14 22:11:27 -05:00
1d828f455c add monetnaildesign 2025-08-14 22:08:45 -05:00
a12ff980c5 more 2025-08-07 14:39:08 -05:00
9cb44fb15c ? 2025-08-07 14:35:50 -05:00
ffd5e085e1 change ingress controller name 2025-08-07 14:24:49 -05:00
0403077495 undo 2025-08-07 14:13:54 -05:00
2124f86879 ing 2025-08-07 14:12:53 -05:00
86c03c882d create ingress 2025-08-07 14:03:46 -05:00
2725aecb58 update route 2025-08-07 13:58:13 -05:00
0c99612913 fix repo 2025-08-07 13:43:15 -05:00
7e93e9b1b1 updates to homepage 2025-08-07 13:41:09 -05:00
d2d3ee7c78 deploy homepage 2025-08-07 13:37:40 -05:00
8dc2243140 add loadbalancer 2025-08-07 12:14:01 -05:00
2a323f5675 add routeselector 2025-08-07 12:06:49 -05:00
cfc70229ee redeploy 2025-08-07 12:04:17 -05:00
593e71509b remove nginx 2025-08-07 12:02:55 -05:00
bca855046f ingress 2025-08-07 12:01:51 -05:00
7aa29a3f67 change name and namespace 2025-08-07 11:55:53 -05:00
0f27698fef update ingress controller 2025-08-07 11:53:46 -05:00
a493cb75f9 deploy nginx operator 2025-08-07 10:38:10 -05:00
31a4d7e3d3 fix directory structure 2025-08-04 14:06:32 -05:00
79706c90ff deploy metallb to internal cluster and rebove ingress 2025-08-04 14:04:50 -05:00
fabc113c84 fix 2025-08-02 18:48:58 -05:00
01bc13e0a2 change to operator 2025-08-02 18:44:39 -05:00
1eb12c2582 veersion 2025-08-02 18:07:31 -05:00
6d43a0c7a8 fix repo 2025-08-02 18:05:49 -05:00
c85d4f7a3d deploy nginx ingress to internal cluster 2025-08-02 18:04:16 -05:00
7ffc2c983b staged next wave of operators to add and configure 2025-08-01 13:43:38 -05:00
4fd272ddf7 change nfs provisioner 2025-08-01 09:26:48 -05:00
99fb15f8f0 remove nfs via synology 2025-08-01 09:15:07 -05:00
bd27975bf3 change nfs storageclass 2025-07-31 21:28:11 -05:00
ee3f2d7fcf fix yaml 2025-07-31 20:39:42 -05:00
81accaf9a2 image registry 2025-07-31 20:36:10 -05:00
23052096f2 image registry 2025-07-31 20:30:44 -05:00
0fc6bc340b storageclass 2025-07-31 19:58:38 -05:00
36ccbcbab1 fix dir 2025-07-31 15:42:31 -05:00
574fdfe25c open image registry 2025-07-31 15:39:11 -05:00
0987226b0d removed sync wave 2025-07-31 14:52:31 -05:00
b200294ebc yaml formatting 2025-07-31 14:48:25 -05:00
27553fc4d1 remove applications pattern 2025-07-31 14:44:15 -05:00
7925990631 fix cluster target 2025-07-31 14:42:12 -05:00
4639725099 deploy Synology CSI 2025-07-31 14:35:35 -05:00
edb019c38e enable iscsi on cluster 2025-07-31 11:53:16 -05:00
1742dff512 deploy lightspeed and cert manager 2025-07-31 00:18:41 -05:00
01e02a6687 external secrets 2025-07-30 23:41:26 -05:00
bca03f901a remove external secrets 2025-07-30 23:36:54 -05:00
4c72e35707 fix nmstate appset 2025-07-30 22:56:21 -05:00
ff925aee83 apply external secrets operator 2025-07-30 22:44:51 -05:00
05fa4eaa43 updated ansible templates for single node clusters 2025-07-30 21:59:01 -05:00
48b8548528 Move ansible to it's own directory structure 2025-07-30 12:41:18 -05:00
0764cc8bd7 wrong app name 2025-07-29 21:56:08 -05:00
28e57361e2 typo 2025-07-29 21:54:53 -05:00
5e1522fefb refactor nmstate to appset 2025-07-29 21:53:30 -05:00
4a2120f830 uninstall all 2025-07-27 22:08:58 -05:00
e09c33d307 fi values file variable 2025-07-27 21:48:09 -05:00
943795984f fix 2025-07-27 21:45:37 -05:00
796881ecc5 time to break cert manager 2025-07-27 21:37:52 -05:00
cf507a70af added applicarions App of Apps pattern 2025-07-27 21:10:50 -05:00
c104009aaa just internal cluster 2025-07-27 21:06:58 -05:00
d085f80ea7 chhange ESO name 2025-07-27 20:58:39 -05:00
8121e5c048 add lightspeed to hub cluster 2025-07-27 20:51:23 -05:00
c51615543b fix target namespace 2025-07-27 20:40:50 -05:00
625c4b8fe1 ? 2025-07-27 20:36:23 -05:00
23b43a16b3 changed default namespace 2025-07-27 20:26:56 -05:00
44b9d099c7 wrong name 2025-07-27 20:11:06 -05:00
350a149645 change to list generator 2025-07-27 20:10:21 -05:00
b358c93c16 fix appset 2025-07-27 19:46:45 -05:00
6feea89ec1 add lightspeed to internal cluster 2025-07-27 18:36:13 -05:00
b09f8e1ad5 added sync wave tp ESO appset 2025-07-27 16:49:58 -05:00
06b28b1297 added sync waves to ESO 2025-07-27 16:36:01 -05:00
f807f47642 fix cluster 2025-07-27 01:14:08 -05:00
79f231eaa1 fix 2025-07-27 01:12:14 -05:00
e6abae943d switching external secrets to app set 2025-07-27 01:10:47 -05:00
e5f84ccbf2 again 2025-07-27 00:41:09 -05:00
9d482087e5 rename 2025-07-27 00:39:30 -05:00
face668dfc deploy external secrets to internal cluster 2025-07-27 00:38:39 -05:00
8293c842e6 Created Ansible templates for OpenShift nodes and cluster 2025-07-26 21:14:15 -05:00
fc31dd2a5a sync to move 2025-07-26 16:55:43 -05:00
efecef6832 Updated mac addresses and disabled boot interfaces 2025-07-26 14:51:44 -05:00
5612932691 Updated cluster creation playbook 2025-07-26 01:06:44 -05:00
0f4d2dcb7c sync repo to git 2025-07-25 10:36:32 -05:00
c625e48dee Update ansible and add python3-libdnf5 to template installed packages 2025-07-18 14:20:58 -05:00
23bdd17e25 final update to creating VMs 2025-07-14 10:50:29 -05:00
840 changed files with 48712 additions and 14145 deletions

61
.gitignore vendored
View File

@@ -1,15 +1,9 @@
# macOS system files # macOS
.DS_Store .DS_Store
.AppleDouble .AppleDouble
.LSOverride .LSOverride
# Icon must end with two \r
Icon Icon
# Thumbnails
._* ._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100 .DocumentRevisions-V100
.fseventsd .fseventsd
.Spotlight-V100 .Spotlight-V100
@@ -17,36 +11,53 @@ Icon
.Trashes .Trashes
.VolumeIcon.icns .VolumeIcon.icns
.com.apple.timemachine.donotpresent .com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB .AppleDB
.AppleDesktop .AppleDesktop
Network Trash Folder Network Trash Folder
Temporary Items Temporary Items
.apdisk .apdisk
# VS Code
.vscode/
# macOS metadata
*.icloud *.icloud
# Terraform # IDE
terraform/ .vscode/
.idea/
.kiro/
# Terraform (track .tf files, ignore state and runtime)
.terraform/ .terraform/
*.tfstate *.tfstate
*.tfstate.backup *.tfstate.backup
# *.tfvars
# Crash log files !*.tfvars.example
!*.pkrvars.hcl
crash.log crash.log
# Ignore override files as they are usually used to override resources locally
*.override.tf *.override.tf
*.override.tf.json *.override.tf.json
# Ignore CLI configuration files
.terraformrc .terraformrc
.terraformrc.json terraform.rc
# Ignore secrets # Ansible
doppler-token.yaml *.retry
ansible/vault/*.vault
# Packer
packer_cache/
# Secrets
*.pem
*.key
# Environment files with secrets
boilerplates/**/.env
# Local paths
~/
# Misc
doppler-token.yaml
# talhelper generated machine configs — contain secrets, never commit
talos/talhelper/clusterconfig/
# talenv.yaml plaintext — only commit the SOPS-encrypted version
talos/talhelper/talenv.yaml
!talos/talhelper/talenv.sops.yaml

3
.sops.yaml Normal file
View File

@@ -0,0 +1,3 @@
creation_rules:
- path_regex: talos/talhelper/talenv.yaml
age: age1xkyuv8r8ce6lu3d64jfspz4e50k6pxlaxwmuplzrtnpfnnrnycaq6mfsrn

160
COUCHDB-ERLANGCOOKIE-FIX.md Normal file
View File

@@ -0,0 +1,160 @@
# CouchDB erlangCookie Fix - Implementation Guide
## Summary
**Problem**: CouchDB deployment fails because `erlangCookie` is missing from the ExternalSecret configuration.
**Decision**: Externalize `erlangCookie` to 1Password (pragmatic approach)
**Rationale**:
- ExternalSecret architecture requires ownership of the entire secret
- Mixing externalized and chart-generated fields in the same secret is not supported
- Single-node deployment makes erlangCookie rotation unnecessary
- This is an acceptable deviation from the pure Harbor pattern given the architectural constraints
## Implementation Steps
### 1. Generate erlangCookie Value
```bash
openssl rand -hex 20
```
Example output: `f4e3c2b1a9d8e7f6c5b4a3d2e1f0a9b8c7d6e5f4`
### 2. Add to 1Password
- **Vault**: `mk-labs`
- **Item**: `couchdb`
- **Field Name**: `erlang-cookie`
- **Field Type**: password (concealed)
- **Value**: `<paste generated value from step 1>`
### 3. Update ExternalSecret Configuration
File: `cluster/applications/couchdb/externalsecret.yaml`
```yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: couchdb-credentials
namespace: couchdb
labels:
app.kubernetes.io/name: couchdb
app.kubernetes.io/part-of: mk-labs
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: onepassword-connect
target:
name: couchdb-admin
creationPolicy: Owner
template:
engineVersion: v2
data:
adminUsername: "admin"
adminPassword: "{{ .adminPassword }}"
cookieAuthSecret: "{{ .cookieAuthSecret }}"
erlangCookie: "{{ .erlangCookie }}" # ← ADD THIS LINE
data:
- secretKey: adminPassword
remoteRef:
key: couchdb
property: admin-password
- secretKey: cookieAuthSecret
remoteRef:
key: couchdb
property: cookie-auth-secret
- secretKey: erlangCookie # ← ADD THIS BLOCK
remoteRef:
key: couchdb
property: erlang-cookie
```
### 4. Update values.yaml Documentation (Optional)
File: `cluster/applications/couchdb/values.yaml`
Update the comment block at line 9-10:
```yaml
# Admin credentials managed via ExternalSecret
# See externalsecret.yaml for 1Password integration
#
# NOTE: erlangCookie is externalized to 1Password for architectural
# simplicity (ExternalSecret ownership model). In a pure Harbor pattern,
# this would be chart-generated, but single-node deployment makes this
# acceptable. The erlangCookie is treated as an immutable infrastructure
# secret (generate once, never rotate).
createAdminSecret: false
extraSecretName: "couchdb-admin"
```
### 5. Commit and Push
```bash
cd ~/git/homelab
git add cluster/applications/couchdb/externalsecret.yaml
git add cluster/applications/couchdb/values.yaml # if modified
git commit -m "fix(couchdb): add erlangCookie to ExternalSecret from 1Password"
git push origin main
```
### 6. Verify Deployment
```bash
# Watch ExternalSecret sync
kubectl get externalsecret -n couchdb couchdb-credentials -w
# Wait for: SecretSynced
# Verify secret created with all four keys
kubectl get secret -n couchdb couchdb-admin -o yaml
# Should contain: adminUsername, adminPassword, cookieAuthSecret, erlangCookie
# Watch ArgoCD sync
kubectl get application -n argocd couchdb -w
# Wait for: Healthy/Synced
# Watch pod startup
kubectl get pods -n couchdb -w
# Wait for: Running
# Test CouchDB access
kubectl port-forward -n couchdb svc/couchdb-svc-couchdb 5984:5984 &
curl http://localhost:5984/
# Expected: {"couchdb":"Welcome","version":"3.5.1"}
```
## Why Not Follow Harbor Pattern Exactly?
**Harbor Pattern**: Only user-facing credentials externalized, internal secrets chart-generated.
**CouchDB Constraint**: ExternalSecret uses `creationPolicy: Owner`, which takes full ownership of the target secret. This prevents the Helm chart from adding auto-generated fields to the same secret.
**Options Considered**:
1.**Externalize erlangCookie** (SELECTED) - Works with current architecture
2. ❌ Chart auto-generation - Conflicts with ExternalSecret ownership
3. ❌ Dual-secret approach - Requires Helm chart customization
4. ❌ Disable ExternalSecret - Loses 1Password integration for admin password
**Decision**: Pragmatic approach wins. erlangCookie is treated as an infrastructure secret (generate once, never rotate), which is acceptable for a single-node deployment.
## Secret Classification
| Secret | Type | 1Password? | Rationale |
|------------------|---------------|------------|------------------------------------|
| adminUsername | User-facing | No* | Static value, hardcoded in template |
| adminPassword | User-facing | ✅ YES | User login credential |
| cookieAuthSecret | Gray area | ✅ YES | Session security, periodic rotation |
| erlangCookie | Internal | ✅ YES** | Architectural constraint |
\* Hardcoded in ExternalSecret template (not fetched from 1Password)
\*\* Pragmatic deviation from Harbor pattern due to ExternalSecret architecture
## References
- Full analysis: `/home/hermes/couchdb-erlangcookie-analysis.txt`
- Harbor pattern: `/home/hermes/harbor-simplification-complete.txt`
- CouchDB Helm chart: `apache/couchdb` v4.6.3

166
README.md
View File

@@ -1,102 +1,102 @@
# A Homelab based on Red Hat Technologies # mk-labs
This repository is the configuration of my homelab. In addition to providing services, the purpose of my homelab is to learn advanced concepts primarily based on Red Hat OpenShift. Automated infrastructure provisioning and configuration for a personal homelab, built on GitOps practices with clear tool responsibility boundaries.
This implementation is built on easily accessible consumer based hardware and will focus heavily on GitOps practices and automation will be used wherever possible. ## Architecture
This repo is a mono-repo that is broken up into three sections: A single operator action — setting a VM record's status to **Staged** in NetBox — triggers a fully automated provisioning pipeline:
```
NetBox (webhook) → n8n (validate & orchestrate) → Terraform (create VM + DHCP)
→ Ansible (OS config + DNS + status update)
```
- infra-config | Tool | Host | IP | Responsibility |
- apps |------|------|----|---------------|
- cluster | NetBox | fire-station | 10.1.71.102 | Source of truth — VM records, IP allocation, VLAN data |
| n8n | tiki-room | 10.1.71.23 | Event orchestration, validation, pipeline sequencing |
| Terraform | city-hall | 10.1.71.35 | Proxmox VM lifecycle, Unifi DHCP reservations |
| Ansible / Semaphore | imagineering | 10.1.71.22 | OS configuration, DNS records, NetBox status updates |
| Proxmox | fantasyland | 10.1.71.13 | Target hypervisor |
All systems on the Server Trusted VLAN (10.1.71.0/24).
## Repository Structure
```
homelab/
├── ansible/
│ ├── inventory/ # NetBox dynamic inventory + static
│ ├── playbooks/ # Runnable playbooks (vm-provision, DNS, OS updates)
│ ├── roles/ # vm-baseline, dns-manager, common, haproxy, n8n, observer, etc.
│ ├── tasks/ # Shared includable task files
│ ├── group_vars/ # Group variable definitions
│ ├── host_vars/ # Per-host variable definitions
│ ├── templates/ # Jinja2 templates
│ └── ansible.cfg
├── terraform/
│ ├── proxmox/vm/ # bpg/proxmox provider — VM creation from templates
│ ├── unifi/dhcp/ # Unifi provider — DHCP static reservations on UDM Pro
│ └── dns/ # DNS record management
├── packer/
│ ├── ubuntu-24.04/ # Ubuntu 24.04 VM template (small → xlarge-plus sizes)
│ └── fedora-42/ # Fedora 42 VM template
├── n8n/
│ └── workflows/ # Exported n8n workflow JSON (vm-provisioning)
├── netbox/
│ └── initializers/ # Custom fields, VLANs, IP prefixes as code
└── docs/
└── decisions/ # Architecture decision records
```
## Pipeline Flow
| # | System | Action |
|---|--------|--------|
| 1 | NetBox | Operator sets VM status to Staged → webhook fires |
| 2 | n8n | Validates payload (hostname, IP, VLAN, template, proxmox_node) |
| 3 | n8n → city-hall | SSH + `terraform apply` — creates VM on Proxmox |
| 4 | n8n | Queries Proxmox API for MAC address |
| 5 | n8n → NetBox | Writes MAC to VM interface record |
| 6 | n8n → city-hall | SSH + `terraform apply` — creates DHCP reservation on UDM Pro |
| 7 | n8n → imagineering | Triggers Ansible via Semaphore API |
| 8 | Ansible | OS baseline, SSH hardening, Technitium DNS A record |
| 9 | Ansible → NetBox | Sets VM status to Active |
On any failure, NetBox status is set to **Failed**. No auto-retry — operator investigates.
## Hardware ## Hardware
- Dell 7050 SFF (7) - 7× Dell 7050 SFF
- Minisforum TH60 (3) - 3× Minisforum TH60
- Minisforum MS01 (2) - 2× Minisforum MS01
- Synology 1621+ - Synology DS1621+
- Ubiquiti UDM Pro
## Software ## Software Stack
- Proxmox (Virtualization) - **Virtualization**: Proxmox
- Cloudflare (Domain Hosting, Public DNS) - **Automation**: Terraform, Ansible, n8n, Semaphore
- Unbound (Recursive DNS) - **DNS**: Technitium (authoritative), Unbound (recursive)
- FreeIPA (Identity management, Authoritive DNS) - **IPAM/DCIM**: NetBox
- Matchbox (iPXE) - **Networking**: Ubiquiti UDM Pro
- Red Hat OpenShift - **Templates**: Packer (Ubuntu 24.04, Fedora 42)
- OpenShift Agent Based Installer (Install OpenShift)
- [Red Hat Advanced Cluster Management for Kubernetes](https://www.redhat.com/en/technologies/management/advanced-cluster-management)
- [Vault](https://www.hashicorp.com/en/products/vault)
- [OpenShift GitOps (ArgoCD)](https://www.redhat.com/en/technologies/cloud-computing/openshift/gitops)
- [Red Hat Ansible Automation Platform](https://www.redhat.com/en/technologies/management/ansible)
## Prerequisites
- Ansible user created
- Ansible configured
- [Networking](docs/networks.md)
- [Proxmox](docs/proxmox.md) (In my homelab, internal DNS, identity manangement, and ipxe are hosted here.)
- Matchbox
- DNS
- Domain Registration
## Assumptions
There are a dozen different architectures you could use to deploy OpenShift in every which way.
For the sake of this documentation we'll assume the following:
## Getting Started ## Getting Started
[Step-by-Step Walkthrough](step-by-step.md) See [docs/decisions/vm-provisioning-flow.md](docs/decisions/vm-provisioning-flow.md) for the full architecture decision record.
### Hub Cluster Previous OpenShift/ACM/Fastpass content is preserved in the `archive/pre-mk-labs` branch.
You'll need an OpenShift "Hub Cluster" with access to persistant storage. ## Security
A Single Node OpenShift (SNO) instance, installed on bare metal, will act as a Hub cluster and run:
- Advanced Cluster Management No sensitive data is stored in this repository. Secrets are managed via Ansible Vault and environment variables on pipeline hosts.
- Ansible Automation Platform
- Vault
- ~~OpenShift GitOps~~
#### Network Prerequisites
The prerequisites for OpenShift in traditional and HCP patterns are largely the same - it just kind of depends on where your DNS records go to.
| Cluster | Endpoint | VIP | DNS A Record | Notes |
|------------------|-------------|---------------|-----------------------------------|----------------------------------|
| Hub Cluster (SNO) | App Ingress | 192.168.0.10 | *.apps.hub-cluster.example.com | SNO App VIP goes to IP of node |
| Hub Cluster (SNO) | API | 192.168.0.10 | api.hub-cluster.example.com | SNO API goes to IP of node |
These DNS entries should be put in your Authoratitive DNS.
#### ACM & GitOps Configuration
Before you start creating clusters you may want to create some Policies, integrate ACM and ArgoCD, etc. This step is optional in case you're just interested in trying out Hosted Control Planes or copy/paste around a cluster for testing purposes.
Find additional details in the ./02-rhacm-config folder.
#### Creating a Cluster
With everything in its right place, you can now start to declaratively create clusters
./05-clusters/hcp-bmh - HCP to Bare Metal Hosts
### Internal Cluster
Two additional bare metal nodes, to be added to Advanced Cluster Management (ACM) running on the SNO Hub. These will be used to create another HCP cluster.
These servers have a BMC interface with Redfish - if not, then you'll need to manually manage the boot and installation of those servers.
This makes it to where you just need 3 bare metal nodes. You could run one HCP Bare Metal cluster with both of the other nodes, but then you have a shared storage requirement that can't be satisfied by ODF since that needs at least 3 nodes.
### External Cluster
You'll also either need you just need at least 2 bare metal nodes.
--- ---
## Credits **Status**: 🚧 Active Development — VM Provisioning Pipeline
- Ken Moini - As I used his [repo](https://github.com/kenmoini/ztp-for-you-and-me) as the baseline for this project. **Last Updated**: February 2026
- Ryan Etten
- Andrew Potozniak

View File

@@ -0,0 +1,193 @@
# DNS Management Refactor - Modular & Repeatable
This document explains the refactoring of DNS management from standalone playbooks to modular, reusable tasks.
## 🎯 **What Changed**
### **Before (Monolithic)**
```yaml
# Standalone playbook: add_technitium_dns_entry.yml
- name: Add entry to Technitium DNS
hosts: all
tasks:
- name: Create DNS entry
effectivelywild.technitium_dns.technitium_dns_add_record:
# ... hardcoded parameters
```
### **After (Modular)**
```yaml
# Reusable task: tasks/add_technitium_dns_entry.yml
- name: Create DNS entry for {{ dns_record_name }}
effectivelywild.technitium_dns.technitium_dns_add_record:
# ... parameterized with variables
```
## 📁 **New Structure**
```
ansible/
├── playbooks/
│ ├── tasks/
│ │ └── add_technitium_dns_entry.yml # ✅ Reusable task file
│ ├── add_dns_entry.yml # ✅ New playbook using task
│ ├── add_technitium_dns_entry.yml.backup # 📦 Backed up old version
│ └── roles/
│ └── dns-manager/ # ✅ Enhanced role
│ ├── tasks/main.yml # Uses task file
│ └── defaults/main.yml # Technitium defaults
└── scripts/
└── migrate-dns-references.sh # ✅ Migration helper
```
## 🚀 **Usage Examples**
### **1. In Playbooks (Direct Task Include)**
```yaml
- name: Add DNS entry for my server
hosts: my_servers
tasks:
- name: Create DNS entry
ansible.builtin.include_tasks: tasks/add_technitium_dns_entry.yml
vars:
dns_record_name: "{{ inventory_hostname }}"
dns_zone: "{{ base_domain }}"
dns_ip_address: "{{ ansible_default_ipv4.address }}"
```
### **2. Using the DNS Manager Role**
```yaml
- name: Setup cluster DNS
hosts: control_plane[0]
roles:
- role: dns-manager
vars:
cluster_endpoint: "my-cluster.local.mk-labs.cloud"
cluster_vip: "10.1.71.100"
```
### **3. Using the New Playbook**
```yaml
# Import the new modular playbook
- import_playbook: add_dns_entry.yml
```
### **4. In Cluster Network Setup**
```yaml
- name: Setup complete cluster network
ansible.builtin.include_role:
name: cluster-network-setup
vars:
cluster_name: "fastpass"
cluster_endpoint: "fastpass.local.mk-labs.cloud"
cluster_vip: "10.1.71.53"
```
## 🔧 **Migration Guide**
### **Automatic Migration**
```bash
# Run the migration script to find references
./scripts/migrate-dns-references.sh
```
### **Manual Updates**
1. **Replace playbook imports:**
```yaml
# OLD
- import_playbook: add_technitium_dns_entry.yml
# NEW
- import_playbook: add_dns_entry.yml
```
2. **Use task includes in roles:**
```yaml
- ansible.builtin.include_tasks: tasks/add_technitium_dns_entry.yml
vars:
dns_record_name: "my-server"
dns_ip_address: "10.1.71.100"
```
3. **Use the dns-manager role:**
```yaml
- ansible.builtin.include_role:
name: dns-manager
```
## 📋 **Variable Reference**
### **Task Variables (`tasks/add_technitium_dns_entry.yml`)**
| Variable | Default | Description |
|----------|---------|-------------|
| `dns_record_name` | `inventory_hostname` | DNS record name |
| `dns_zone` | `base_domain` | DNS zone |
| `dns_ip_address` | `ip_address` | IP address for A record |
| `dns_record_type` | `A` | DNS record type |
| `dns_ttl` | `360` | TTL in seconds |
| `dns_create_ptr` | `true` | Create PTR record |
| `dns_debug` | `true` | Show debug output |
### **DNS Manager Role Variables**
| Variable | Default | Description |
|----------|---------|-------------|
| `cluster_endpoint` | - | Full cluster FQDN |
| `cluster_vip` | - | Cluster VIP address |
| `dns_management_enabled` | `true` | Enable DNS management |
| `use_hosts_file_fallback` | `true` | Add to /etc/hosts |
| `dns_ttl` | `360` | DNS TTL |
| `create_ptr_record` | `true` | Create PTR record |
## 🎯 **Benefits**
### ✅ **Modularity**
- Single task file used across multiple contexts
- Consistent DNS management approach
- Easy to maintain and update
### ✅ **Flexibility**
- Works in playbooks, roles, and standalone
- Parameterized for different use cases
- Supports multiple DNS providers (extensible)
### ✅ **Maintainability**
- One place to update DNS logic
- Clear variable interface
- Better error handling and debugging
### ✅ **Integration**
- Seamlessly integrates with cluster setup
- Works with existing homelab infrastructure
- Compatible with Traefik load balancer setup
## 🔄 **Integration with Cluster Setup**
The DNS management now integrates seamlessly with your cluster deployment:
```yaml
# In fastpass-first-control-plane role
- name: Setup network infrastructure for FastPass cluster
ansible.builtin.include_role:
name: cluster-network-setup
vars:
cluster_name: "{{ cluster_name }}"
cluster_endpoint: "{{ control_plane_endpoint }}"
cluster_vip: "{{ ansible_default_ipv4.address }}"
control_plane_nodes: "{{ groups['fastpass_control_plane'] }}"
```
This automatically:
1. ✅ Creates DNS entry for `fastpass.local.mk-labs.cloud`
2. ✅ Configures Traefik load balancer
3. ✅ Tests connectivity
4. ✅ Provides fallback to /etc/hosts
## 🚀 **Next Steps**
1. **Test the refactored approach** with your FastPass cluster
2. **Extend to other clusters** (Hub, Internal) using the same pattern
3. **Add support for other DNS providers** if needed
4. **Create monitoring** for DNS health checks
This modular approach makes your homelab's DNS management much more maintainable and repeatable across all your Kubernetes clusters!

View File

@@ -54,7 +54,7 @@
# (pathspec) Colon-separated paths in which Ansible will search for collections content. Collections must be in nested *subdirectories*, not directly in these directories. For example, if ``COLLECTIONS_PATHS`` includes ``'{{ ANSIBLE_HOME ~ "/collections" }}'``, and you want to add ``my.collection`` to that directory, it must be saved as ``'{{ ANSIBLE_HOME} ~ "/collections/ansible_collections/my/collection" }}'``. # (pathspec) Colon-separated paths in which Ansible will search for collections content. Collections must be in nested *subdirectories*, not directly in these directories. For example, if ``COLLECTIONS_PATHS`` includes ``'{{ ANSIBLE_HOME ~ "/collections" }}'``, and you want to add ``my.collection`` to that directory, it must be saved as ``'{{ ANSIBLE_HOME} ~ "/collections/ansible_collections/my/collection" }}'``.
;collections_path=/Users/rblundon/.ansible/collections:/usr/share/ansible/collections collections_path=/opt/ansible-collections:/usr/share/ansible/collections
# (boolean) A boolean to enable or disable scanning the sys.path for installed collections. # (boolean) A boolean to enable or disable scanning the sys.path for installed collections.
;collections_scan_sys_path=True ;collections_scan_sys_path=True
@@ -209,7 +209,7 @@ private_key_file=~/.ssh/ansible
remote_user=wed remote_user=wed
# (pathspec) Colon-separated paths in which Ansible will search for Roles. # (pathspec) Colon-separated paths in which Ansible will search for Roles.
;roles_path=/Users/rblundon/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles roles_path=./roles
# (string) Set the main callback used to display Ansible output. You can only have one at a time. # (string) Set the main callback used to display Ansible output. You can only have one at a time.
# You can have many other callbacks, but just one can be in charge of stdout. # You can have many other callbacks, but just one can be in charge of stdout.

View File

@@ -0,0 +1,6 @@
---
- name: Create jarvis user and deploy SSH key
hosts: all
become: true
roles:
- jarvis_user

View File

@@ -0,0 +1,154 @@
---
# ============================================================================
# Semaphore configuration-as-code
# ============================================================================
# Drives a freshly-deployed Semaphore instance into its desired state via
# the Semaphore REST API. Idempotent: every object is checked first; only
# missing ones are created. Existing objects are left alone.
#
# Loaded from group_vars/all/semaphore.yml so that the configuration is
# version-controlled in the homelab repo and survives a wipe-and-redeploy
# of the Semaphore VM.
# ============================================================================
# ---------------------------------------------------------------------------
# API connection (defaults to the local Traefik-fronted service-name URL).
# Override semaphore_api_url to point at a specific instance if needed.
# ---------------------------------------------------------------------------
semaphore_api_url: "https://semaphore.local.mk-labs.cloud/api"
semaphore_api_validate_certs: true
semaphore_api_token: "{{ vault_semaphore_api_token }}"
# Feature flag — keeps day1_deploy_semaphore.yml deploy-only by default.
# Set true to also run the configuration pass.
semaphore_configure: false
# ---------------------------------------------------------------------------
# Declarative configuration of the Semaphore instance.
# ---------------------------------------------------------------------------
#
# Top-level shape:
#
# semaphore_config:
# project: single dict — the lab uses one project ("mk-labs")
# keys: list of credentials Semaphore stores
# repositories: git repos Semaphore can clone
# inventories: Ansible inventories from those repos
# environments: env-var bundles
# templates: task templates that tie everything together
#
# Each list element has a unique "name" used as the natural identity key.
# ---------------------------------------------------------------------------
semaphore_config:
project:
name: mk-labs
alert: false
max_parallel_tasks: 0 # 0 = unlimited
keys:
# The ansible-vault password. login_password type with empty login
# — only the password field is consumed by Semaphore at runtime.
- name: ansible-vault-pass
type: login_password
login: ""
password: "{{ vault_ansible_vault_password }}"
# SSH key for the gitea deploy access (clone the homelab repo).
- name: gitea-deploy
type: ssh
ssh_login: git
ssh_private_key: "{{ vault_gitea_deploy_key }}"
# SSH key for the universal automation account 'wed' — pre-baked in
# every mk-labs VM template. This is the canonical user Semaphore
# uses to reach the fleet.
- name: wed-ssh
type: ssh
ssh_login: wed
ssh_private_key: "{{ vault_wed_ssh_private_key }}"
# SSH key Semaphore can use to reach the fleet as jarvis (admin
# account provisioned by linux-baseline). Retained for jobs that
# specifically need jarvis-level access; the default is wed-ssh.
- name: jarvis-ssh
type: ssh
ssh_login: jarvis
ssh_private_key: "{{ vault_jarvis_ssh_private_key }}"
repositories:
- name: homelab
git_url: "ssh://git@gitea.mk-labs.cloud:2221/rblundon/homelab.git"
git_branch: main
ssh_key: gitea-deploy
inventories:
- name: production
type: file
inventory_file: ansible/inventory.yml
repository: homelab
# wed is the universal automation account pre-baked in every VM
# template. Semaphore uses it for fleet-wide jobs.
ssh_key: wed-ssh
# become_key is Semaphore's sudo PASSWORD slot, not a second SSH
# key. wed has passwordless sudo on every host, so reference the
# built-in "None" key. (Semaphore rejects an SSH-type key here.)
become_key: None
environments:
- name: default
env:
ANSIBLE_HOST_KEY_CHECKING: "False"
ANSIBLE_FORCE_COLOR: "True"
# Semaphore runs ansible-playbook from the cloned REPO ROOT (not
# from the playbook's directory as I first assumed). Path is
# therefore relative to repo root, not playbook dir.
ANSIBLE_ROLES_PATH: "ansible/roles"
# Collections are installed by the semaphore role into a host-side
# directory bind-mounted into the container at this path.
ANSIBLE_COLLECTIONS_PATH: "/opt/ansible-collections"
templates:
- name: "day0_linux_baseline"
description: "Apply the mk-labs Linux baseline to one or more hosts."
app: ansible
playbook: ansible/playbooks/day0_linux_baseline.yml
inventory: production
repository: homelab
environment: default
vault_password: ansible-vault-pass
arguments: '["--diff"]'
survey_vars:
- name: target
title: "Target host or group"
description: "Inventory target (e.g. figment, semaphore_server, all)"
required: true
type: TextVar
default_value: "all"
- name: "day1_deploy_semaphore"
description: "Re-deploy Semaphore + PostgreSQL on figment."
app: ansible
playbook: ansible/playbooks/day1_deploy_semaphore.yml
inventory: production
repository: homelab
environment: default
vault_password: ansible-vault-pass
arguments: '["--diff"]'
- name: "day0_linux_baseline_check"
description: "Dry-run the baseline — shows diffs, applies nothing."
app: ansible
playbook: ansible/playbooks/day0_linux_baseline.yml
inventory: production
repository: homelab
environment: default
vault_password: ansible-vault-pass
arguments: '["--check","--diff"]'
survey_vars:
- name: target
title: "Target host or group"
description: "Inventory target (e.g. figment, semaphore_server, all)"
required: true
type: TextVar
default_value: "all"

View File

@@ -0,0 +1,4 @@
---
step_ca_url: "https://turnstile.local.mk-labs.cloud:9000"
step_ca_fingerprint: "f63c44e76381e359978bd2dca07c928d04ad44b575f0364fa66b5725c7e7891b"
step_ca_provisioner_name: "admin"

View File

@@ -0,0 +1,45 @@
---
# file: group_vars/all
# Proxmox variables
proxmox_user: "root@pam"
proxmox_password: "{{ vault_proxmox_root_password }}"
proxmox_host: "main-street-usa.local.mk-labs.cloud"
# DNS variables
dns_server: "monorail" # .local.mk-labs.cloud"
#dns_admin: "admin"
base_domain: "local.mk-labs.cloud"
# DHCP server
#dhcp_server: "matchbox"
# Terraform variables
terraform_server: "infra01"
# Traefik variables
traefik_server: "lightning-lane"
# ---------------------------------------------------------------------------
# JARVIS automation account
# ---------------------------------------------------------------------------
# Public key for the 'jarvis' user provisioned by the linux-baseline role on
# every host. Public keys are not secret; the matching private key lives on
# the JARVIS command centre (carousel-of-progress) and, when needed, in
# group_vars/all/vault as vault_jarvis_ssh_private_key.
jarvis_ssh_public_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID5sym5ajFvDyzw395BkHv7qVb66XPTx/OF1p19MGuNo jarvis@mk-labs"
step_ca_principal_mappings:
- local_user: wed
principals:
- ryan.blundon@protonmail.com
- ryan.blundon
- ryanblundon
- local_user: rblundon
principals:
- ryan.blundon@protonmail.com
- ryan.blundon
- ryanblundon
# Leviton My Leviton API
leviton_email: "{{ vault_leviton_email }}"
leviton_password: "{{ vault_leviton_password }}"

View File

@@ -0,0 +1,441 @@
$ANSIBLE_VAULT;1.1;AES256
61346566636664323837633233623331666232363731633634366538333962363966366433636232
3363303535353934306538656332633436303638373933610a326364636563386437386466653335
61326564366131383062363037663965343330663431396331343831326133393233366439646538
3435366135636162300a326161313661633134393666623964323761343139323630353938313864
38393339343231636566386534363831616237343531373835326662376162373264633038353330
33663539646165656261326663306332313932333761383937633265373330346134333532616138
36623861356264643963366138646233343436643066396430653663323039333165306536383565
66346634656131323066353837313836356162346330376232363835653331323736383839356533
36636536313630623730333631383166383032363633643263613335633938616235656166356164
37376563633634666465663332633635653736393135353538346366386630393765316364616463
64343563646233623330633765326537383739323835306566613730323630376339346139613163
39623132633830393635313537616661326536643662323237353337316234663931626261306639
32376434363964363437373165613062333966653331373664383633656130313764626639353164
61363261636362326261383430666235316538383933643365663830336665633164613435303065
65313761623032373332623932623765616263633433663466366633363333356330393161346536
32373966366330363565663632306130656161323234303666313636353166353064636266386239
38363935313564323930396531336566313066386630626431346633323331633436326237633862
33363334346264343537333065336536343264336538643862386336666635383766333235323034
65313764343637343361323064333864343464356336376333613361356530343765626166366337
30326266316135353263323131326664326635616231303831353036333439383633376161383662
34613337376237393537343932326330323564313931376261393163653464316232373238393535
38376637666533363166646336376665363538643363666537393232643762383130366165373037
39636531336664633539653162393536306531313637663039636431353365396466363236376432
66633064316161623739623138386664623165363762663637353435373863656661393362353330
62333163366438343364626432643232396133643065636133626339323935623061323761643331
38393466353833643432643265623431323436346638626661323036303334303239383838396335
33353031373435306266303466366264363737326562326666363066646362623335306465363666
34343862336264626134336338626330396130393833383434363834336133393032613237393031
62633133663433653662373636356231396630626463633432326665326461396530613162373366
34363462353865336166313639663438653839376531623533376335323863383632376433343432
63316239333566356362306133323332353937366237616237326566323363646261376434373939
65623031396139396164653939393162393930326530656135363564363565643133616261666436
39313366383334633036373832323462623161666563303336383131363163373233616435663065
33316266626162363063366539323462346464393932393165643161623964373539393964636139
34343538663533343931613439663436383531393939333239613234386465343430303833353133
64636338343032373230346562363338303166616166666438646262393333313633656162303764
61386164353835383233343535636132643831623062393539336233626235303032663464386266
66366664346364623533613832373631313336336530386162363861303333306563353439333236
65316161383538373733633137633065633362363936373264333034646135366434656534383030
35303838326361313163636432643638373832396165626532623261366332613562383261346661
32386164366264356536653439353763346539643934393466643166333761366436613165623064
38623530316264333939383932313133613730383632663830366365343937313036373439663930
66613034633532303065333935623264636534393638386433346336353765316264346662313337
34613732356265363839343434633563303665313030613535343336363432323366623234623738
38396439376335653731346464363533313161343162653930656137626337623961336232623165
37663863313532626638303937326237353235393866303063313532343164303165636262646434
33316363343265336462626630376665336162336661616436656135366137633834326465303964
62643435393537613836656334616236333131653133653065636361343261643032646163633462
38666465633531383235613732366131366637336638393639346463663830626539653538636165
34633238396233646337383936633438393039306461633631363161656465336238333434633030
37346334396538386437323337393562353463336130363837393033353834386631396164386466
39343338383634663338373536656363623234313339343363636432303937316462376139333138
36386331616639376331393963356138666463306666306436643237303764346330393332626465
62666235656334323231626635333338363736663063656664326139346363323039393565363966
36326662353631646135623832333266366139313662316333376237326433626434336531366666
63323062393030656639353634336339343034316237373932376466653930616465346436383664
31343339663333386533323261393164363039386666323766343337363033366239386266393362
35653333666432376337653332393930383561626431376364326439633331353033613361376333
36633965616237333662613931366533366636393432393863323330383431326635386466646565
31633136373466663161643965396434333437626636626237383630663730663531636139666166
63613964646133666361303430643766333063643732393062333631616132323762623735613566
62613362366536386565636538656138366532643932373163353437636562353737343566613330
34366436323063356433303863383961303130666637366235353264356433313066616262313532
37653165356230313165323330666339376337656332366531383566353536633831363430353764
61383533636263393336313631393766353638353862363666323163356631396234326262626634
64633438333438633235666361373333316265326237393134346539343361353461666664306239
30643939633462303463626237653135333863336530326438313233343232323634633935653134
65626337313266356462323039393564356638353263643566313264616239353036323063623333
32636561373166653739396635633039623464656536653531356365393362343132343631343433
30623438613137333063653061376434336233666633366530333634346136663831643866386132
66383865633531356533316437383933656439353262626138623231613532643834383238323438
63616238653338656536316662306565326233646136366634666330666263356235636231393133
62373665333336336166363936656636373865666132646465656563653464633338623831616436
38663730353764386164653166666136336665383439313135346139373239636137333035323862
62366231323434363864656532323462633061663765616365366335376533336661656536353537
37383466356438343539363935653534336332386130306235353565383933316137313233383832
30663663346339396236633733633464393631376436353833663462316533646534383134356165
33616139663236653661383063323865633339343836643134396364356230323135363731626236
65333338656430376466616137303561356433393733373961313862663430656431343363363635
63663561396438646536346237653562663661353438363237616539393335386338363132623937
35613838616134633334623939333466646131363663653961646138613064366361646235346130
64353130656532333664376635633137333434343965633361333830366232356434346263646232
33336635323062653161346138373636613638623431383538343436323737316335306334366339
37303033646363636637363333323533363037616536303737653064333230313761313366666664
39643861393636356663396431346139646562383035396461323165663665626238653536353133
38346564383761636537333961656538656366333530383831306533623532303635333539616165
65303537663934626561666335643361333339316434373263613637663037613033633930313437
62376233613638636234303264353339653963383633303761316339626434303466376431343664
62616433623431353164383735313835383935333538636432386562616438623062643866333137
65366263656631616334303538363239316632373936663431643239303034373831623765663662
65646133393263373639393664373265376664356434303131396465646161616464623734316436
30396265396536333731363535613035333066616136306538623165306462333561306337386131
38313135663030333439643361633864663061323738313862326131623637663634323131373263
31383730653838656630306138386435643435343162626638656338333131363137363063616432
33326636383530343335613765346634316530376635636233356364373066343835376633356137
61356336616138383536653161363930306364313035313135656634363632353731393934656562
32643362636466656562666466613364333739393736313130656137396437373763323138353362
65353661636537623230623666373534646635626538616431323966326230396637326335326239
30373534616362666435346263646238316637646330336635346437333537643630303062303338
66626237303830386164656531356439656162656633303564316636663639383662306462373430
30616465353461366532356339313734386137623566653262656365643136303065353834646365
66353637306139316132386239306163343564356464316537656237623734326363333137366463
63633864373431353064393561653366653233616233616536373837353065353330643863663361
63653336633135636564373632653561393862633034393266613337343132363034616139366665
32313536643066376638303465616632316533636530626165663561383562623133383834376362
37353633636561326263376366373430656562306633666431316664313832353039366437396436
36333233313765363833316536363132623633336439653734623964373461396433343536316635
33356266663630376662373632323436313431376435646561336665366634333135316334303863
66653461386632303362366535346434616438653135613264666334363363663633666130313734
36376664636363623965363630326236346566393961326332613638636163666562346661363663
37663939306338643134316163333233363164616232353063323134373565616532373636653964
34363237353234393632326438346132353036313232313231663762323131303830613532386262
65663836636266616661373939383937363330623665656139613239396231326631613461396435
30343064366431343735383135623135643165376464343138393131666235323832623034333466
65663932623335323361313930343330636232626661323464623236313262303638353933613630
38646365373164633131653430616338643163383639643134376464336537363861316137353063
62623965633339303838663434306161633733373365383863633462303161643965326433333832
62383731376261303364373931376631626138656535363638376236393133636261626234616137
30333637656139343535636534346239373631633966616462636637306639336630336337343832
66373730373161613935393230623934653530666464636363653066353566333730336261386138
39626537306532633937353761303765393530316236373432373734373965623238303430623632
35396435343065346136616632376533633465346336366137663836323234613735306537373631
39653632303133633032316634323966333762623130613161323064346562346163633361323333
37613735666361366530356363393262393233346239653137303330336161613461666330636530
30366531633538636433323831646433646230376335646535643831316238323233383534626139
37343635383333633034346436376433303634383336316535306236323061623365303432326632
63343036623136363966373363373864623262646462333332616635623831383338666438333962
35326534656537386636363234316332653232626663623261343737636361633338343933373066
39346537653261356366656135303961356162363763623532613631636239633432393266623966
31303434343438643563303463326564393632653938656661366630323636643364333664306461
37616563663630643462653634666166633333393466373462363937336333303963303735666234
65666632343036646536393965643837313234353537663839353032323362663237323338623139
32656663313266393763343133356438303637306633306231323033643535346637386332643934
36653638623334343030363330643237636635633464336561316331383432636438333839336535
37346436393066626634623136366664383734653936393335626636383736343266653863396331
34303234393663653438333466313962313731666165366233313162613437633430333562343163
36663330656639643633633336643436636564366533383235383262313865656538366531303337
66353732626333623031326261306633313666393130373562626564343539313338366364656336
61353837633765646564653936366236303463363531336537333965666437303536643135393062
38343836316265396665386664326532616632346265613334353466643165663463613661333134
33623333343037333133656434376537303462343061393838336438346635363732393337313532
66346366663538373237666565643662383665623130346466316663346162313964653830373630
65663134303466303630353764636639613661613939396438633234636437363937356465326132
38653132613066356633333434366265303235363664636432323566643838636266363762633064
30383332373631326635613266366331656664393934353731653063366537616435396432663731
36376437326237303263393334386263396334316333316132653765343564373935353563353531
63396539613739316539656332623262636136363839353339353331306439323966656530383564
34336362653064656138363861353331306164663834626662646263623561393764303864306565
33366230303663303839313364353430363034316437326135393530366335363065303335636564
31336135333163643864396264383437633736336639333539656661653638333936643030396339
64646336323338376530653735613739333939363366363333623036663861613061366531643034
36303037363361333561313961343730363533383435376362333739363863643365616166613036
64663134666166383663656338323933386362393162636132306331633065323139663162383632
38373264376666363964653138623232313732626665386333643435663531373532346539636636
34303839653338366330613734646636303163643532373030613239313239646630366438386662
36616263373562626435346638383836336635386661396565653836633339353938383164373137
39323364666636356561343139373830383865633466656532336361336363363964353933613133
64343833343330353433376166323531393066356437333132346133643364383562393731333234
39363830653233386566356130356439353535353636656362616534326336616163316233353962
32663361363836646166326663633730333436376230663235393738396639616331303066626662
31643139303835303431303535393536643163393264386463323862303439353536366131373033
31653234323165373562313063636436313538393639663962393139323231303863336165383830
32363535616361666533316132343965343661373762323064333161383463343731356161326333
32623464666562626531343664643933376162646631313166643365666465383035613464643762
30663065616166613531343339383662303438653531333866396561346637623664376266373839
31643233366363646632663635343161303033323061353737666233663561623462663837643236
64613363383631663039613031616133386130363730333361393739356361376165353036326237
34383533316436333465313939306437383337663864643935346336353935303664663866663564
34346362313730306132373936663764633662646466333135343332393765373333333363376135
61303839363236373066643066363135306266333364666132323033393836356531313232363864
61653565323263306263653535306634313662366331643663643966353164326161383530613730
65303738336561363161336330373832393062613130303131633265303037396230313839306661
62373938613034383966336137303431613161343937343461663066343436626563623366646132
32386238353662663337363363393039653330383331636430343632343064383565626338313030
65643333316431616637643162353634323333313165343833636465363461333063306538396636
34343832633565383961656537373562636433323566663830326136353765303230393939356132
62356130396264633930356362653066363163623631613434326464383361643262373936636461
61616631363335353366323865316439396335373966393464626335353631383137376532346262
62343930633430333335623639643431306533396531333666653030643161356339353933643837
33343734373435303235646239343838363439333433333437643162326539326261656330643938
38306131343464613330636636396266353638616230333263323334353862363738623362633034
38626566303336393163366265306564646436623630656533633630653836333035616364623031
35363735343835306132646664613261353230336537646161643463316566343430373461326233
62396563343939643432663062633064383738383834336634323635396461383830363063636432
36666566396535363835303034363732653433623665353437626533633032333838316564323834
30356132613563616333313039666330666565666534393139653065663739363061306365666135
63396262393464653566626635643365356533623761643763663662383832393164326462313837
35616233396165363434633434613031663739343432366263303734343635336136323830333163
33626461646661343564303134366139666639383630663438396139306566643636623431396332
35633232333064383161383933333931636561313035343561643534656635363432333266376536
37373537336239363166306430376132323862333732393164323964363966666262346564613631
39616438623830386266643634663562396336666134323137366233363061613332376661326435
66313565346663343538383235356232346438383835623364353334376565393464393966366538
65376334623438663562623930616266353961393430353361623034306231326530303438316339
34386462666166363534643764633563613934366164303531343366383464643832353739316433
36326364663737353562396261363736393562393964633539363031353432356238333461383730
39386437663434326635373465643234373034303963396434656161633839613731326365613736
36343664386565313535343635376532386331643264613563613735373631626466313964383639
65663761663033333932336665383631636563363561646635383965393039393363346166613861
66393466326634633438343632376538326634353937633562333137373565343339626635376133
36626638393932623837336162343831303436643535643232616432363061346165373965323365
63386632336532656635643863623132393931383838346263636339646332373665396632653361
32346330306362343538353833313833383334366666613436343066636661366238333764353863
37333534366536313464383364303335356232363866363737376665616438666533633066373164
61636362363237313362633865333630386430623431656437646437396131363035616434343136
33353839353765623531323061306465653336623333393837363538653830326561396137613138
64646133626130323234383763623935323433336535343631383535356437333864633330393134
66623530653636623565353830613538616433613538383632303539386362383636376465393036
36373930326430373336666138653334616530653930336364353464613163656131323566393933
62646265393539313432653462616431636232326131313239363337396635336131383534353338
61356535663130336266376431666533353864393766313962623563313131336533656461373836
34653833353730363433663435313039363861386336643937626430326563353230386630356437
63396636393561376330316232343964666337663532353438303964613365303632323263633161
36323033313935636239633232333632373637613439633332666665356331363532373138393861
32326239616338366364613765393665396461633034346436623037666133623565613266376536
37633966626636323762623965653831646434356232626338636332656661333464623261393933
38643631313063336161663830343835366336346632636663353561623835303130313163366635
38633038366234303334336432653534353734346236346638356135633234323364633937666162
62323132393032303139393139633663303138646133626364626533356465323464613338363533
37306362346431306164613637303965373132323633613439396166383134323162313164323431
34383136323636636539353264396439613231653663636638343636626562353861383736306331
31303564313861306666666332303136343330363063313964353331346433626366303935383066
35386261633137613331303630613137376434663836383930366263666262656666356139343164
35616334373931343336636637356339343266613161623561336264623866633834626664663333
66346231386433383163653437353336613731646435626431366663666465313864613837343365
36633962326633313735393238373166636435656661373732386637626264323363623632353631
61613062653164353761313730613936356632306438643431306332326363616464343237386539
63393866393461663135616235643465373838336566336330346662616130666130396364343634
64333335656565623662646136313766623031363037366535386461393430646231353536346332
64313431326164306266666539366264616139303065626536333664623737336134643761333536
66333331303831316137646137316636633161353766393136363038653765376465623561373761
30633331306565393365323735613239393338326635626136363933303862343465623166313539
38393533376630313666643335623165336165633933323036643539623136333938303233353063
30373032633036663963323831646234653537646230643363663238356631303434373265303735
39613263383261306332346264646562636432663238663536653934386630393534653832333332
39316530373861323830303136663462346164386261636666356235633563646264316636363066
39353464346131643239363736383335643132383862303435366230353631613330333133396336
34373532613134613231636636373132643462353865363439356336383266616631363139656265
65333332663139393135643366623266333561313538646639666636303038663962626438663934
35386463386635613236313666306266353064663666333861643932633862336236333435363539
66356130646235636637626339356632376638316231656266303637623965636135636333643065
34396633613536336163306561363332356430616334393133396639663731653863336531313564
35653333356232396232613864313834656533323034396134313061313731613732386366343134
35663836313639663038653036633338336666633066396136663964646137636262313762306261
31313266643663373736393637323539343035636464336563636162343830313831383631313865
66633337613632343363356331663639616231653033663962376131646533313237333536663438
39353034383661396237623438623361323232653135653430313738633835613961323735343365
63376131386339643330303938343765393365656466393965363838343431373763616139393066
37323037396165636630663733333661626462643437616633383132396532396263373936343538
30333732303235376435666565313435356137383538313039626462363066373864303633616638
61363438396634613862303965653937356662373231346539653339333034343961623435393739
62393430633365323034646233393537306266326234643339623339663764343935386430383461
65396434363634363439336137613837316532373931626434323366393564646630313330366162
36653532306439653964373631653061363664663039633134633233346166376261323938363438
65653637313432663962646536376634333735616531653133653061313534623163323738643466
65363538643336353338346632323565313234333463396464663732613332646237653332623866
38666535386337386361333565646633366538333333346336363636333237316462636365383939
61393631356163313933643230343133643539323834363437306465653438396565393532636464
62343263353638343565653065373461386461363736313931313163656138656333383733373532
62356563643663373765336334306466303437343733316431623665626465666633373463366136
63663738663431306532616431373031666462346131623665376235663634336265303035346432
37616536663965643236346264656361613732386239653234613932326462613637666231396531
33316162353038306535386661626464316130343436353230383934346263323738643731373534
36616537626437393530666162336338383563333636656230646235353266373532653431633539
34646531303630373034323932303863623536666261326463303031636666363738363664653738
31376335316239373437363431626630613161663962663934643431303966323237616339393132
33366661383263396632633838393536383335343761306461313638613236326266396363623634
37623165613935373730663139303737373864663035323030643264663932353465666138303264
35326665656538353762643637393664636137313034643637643730313336356464383131343463
30623232363838373338366163326538316465643930656230323831666462353037633131643961
39393238386637313632663336373661636230386632346432366633356139376430646338316235
66393663366232366230326262633638653863396635323539363465383535303433643061633839
61666431353737393939646638653435343039353463616165653630373130383238333336303533
36343331313235376263303633326139613734663264353330643666633234323730366134663262
66643730373761323439636161623135646337383163353361633663376466343839623437613662
62633837653464316430626266646338333530646234623834643739303738366165646235323232
63663433336664376665623734613965626464653832643366353432633437643739396265376531
66636436336135343833356334636531376131316463343162353362356439386139346365333938
35646431306532663936616264626538666638336130313133323830313966353161666236323735
36313666346638363865336331313163346635613735363634366265373363303033616663363563
63353530636566303733343962653138363532326339666230666661393766333863363539373565
32363733613164373762646633373833373466326633383339626431323462306663353235346664
62653361323631623063313435663735333638616366613634363166656431303031353833646563
39313630366430613234643361666365386462346338323330646366366335656136643462663166
63316135306437376139643337646639303261666434316266323465373832333534373730636465
34313832633839396634316561346633643734373763666136363039653136353866343132326430
37303737383030616635343539386337336236376236383961333336363030376333666432613362
61366365316364613639326236333738313936393535646636383261356635303238303938383130
33613264396339646136346631643938356138653535636636393837346430396433313661613337
31323836323162323632343462666433633537623763653566643231656134353235383131663466
62313730366338646132626338623934646364316637343364653236316566666338363339363731
38373832316239613961646432373864306235656339373963643938343664346238373761663830
35663732396430666536313239386465373235393466643930383439613862313337313638386662
34623033653238333064343861633132313762646534646263663566633264393638343730313939
63353438333331383861343135326439303965333834646430393531383762623765666237316165
65346263386636363638663530643633336331623734343231626363353165323562616138373331
33393464333464666235666539333663663061383335343933373066333966346131653361333935
62643363653236626330656561383635656431303231353731396466363232613462323938643730
63356531616364346264653666613564613239646465376238336337383938313035366636396638
65303566343738343731656338663832396330623066646233376436323334633339343162383966
35343164626365386162666162346366656539383334346238336131616338393261306537303534
62343164386462393039393763303566346163646330336264623462353464306232363934343630
38396565613830346634653934653737636262646438643866376138633065303239633538373432
32653562343666313361383331636261313331363639633465383161393063336335343938653933
39663865613236393037333031613964633961326332316662666531353839343937393761636164
63643230623332626266323061386665333862303532313530653534343762383066643736336466
36626564633265303330666331353163396130376331333136386638306233343138613664626537
31626236643637363766636635333465396233663239353865306638303331363937663936613563
30306166393033336164333432626239333338653837336234393536343438326436343434373433
62613165633736353838313439366434323664333832366132663766613236643430313764343032
32636630393032343130623438333166306564366562373764656135653730346237343737346638
66333462383865333733316237663833323036643230616531353630306565353236363734643839
38616532626537613336366637386633653065383135643135363332353731353663323365653763
34623433343135636338303662633966343965303461313136373333396538626166633330343635
61633034356135623639623636626463373339633665366362343064313262333061666637323163
38646266613461393533663061376132363234376164393939663763643962633361663665336239
64356633323730303262383766386635613361316139353331346466643661333331336161636137
63616230396361626133373934613164336335376565656230623030393861383234346138303564
39326134383063393765336461306237653839336531363538383666313832353732663536343134
38663535316333656164396562303036653137653366393462663238613632383230626130346266
33653136383237303139316133636230373866316164336663613666373565643733663739323362
37633734356234626432646566333038303465333563323931396630386430633032626536623765
38623961373063323464353465316432393134633432343465633030363539363865383661646138
64373436336132356535646164336631626639383765386166326532643436393638653431623533
65666639346664613266616364663033363730373637343962393533656463653661613737303837
31633262653361373036383636323766653538346532353038393835303931323264376535373062
36663631316234363063393938633034306465373966333062663162653266386237663137653033
66346435313334653962373935316537376132353962653535366564306132636534306164666431
38363634333635373966353636633836373865666237656336626139353130626332383338313238
62616632616337363664616165336563616161386662306535666634613965613039356165396239
32316364623534643662323334326461386335666530373662353931363131306364626638636430
61363533613638616632396463343239633865643438373837333535336662366432373738393361
31663064623431383336343034653734306635323764613031353862646663626339323764313731
33313132613138633364646532633334643663663339633264343365363231633034666265376161
37313261616230616664396138306264653139643438306662313462386439336434373664313532
61396631663663353334653961363663393330353830306130313065366330653761393436326535
38326163366564373236386431366436633138326561303237613965353266346361303063613666
66333637313061383138373661643964326162633137626163383936386132346636386335353134
37393661666465366331383164366666666431386166396337386630643438643564336139656634
61613232653433336330376433653062333062663034306630653538303337343661326634363463
35626130353561353436386261663335623964633564303365316166306237306634633464653565
61353532613631653037643162666366313339633034646435316338353034366630303736386639
66396236646132366265653536386435333066306461303334646535643835393730643338376633
37303161613837383838323961343834343562666532366136303337363832363765326637653934
66396331333938633034303761313566656533346336393531326235393862623462306434396438
33393539306234626163626664393037666533326133336464323831323132633035313561303034
38343332306532303939363038383934376136333661343435313238356536396537333066613665
31623564306435313466343133643730313262623765346464616263313831313164353162636334
64666133333330646666663336613332373664653932613834383739393762326636303466363134
33393164326232353065396361636539626531656337353363666233343139333263383664666531
63663539303165643564356261346433373438396532396439363330623035353636386531356437
30326231333333393533376366333062386464323565306463336164636434303137386661393139
65336135313462363237343861313165393862393233656561663662333162306539316365393063
62623837353437373236653965646131383262653733303038306134626430623961376366383331
62383366336637626531653532633664616537333032386434306330616439373430383664623638
65336666303263613965376664336262353139396535646662316233343764396661616131346237
62666561373866646564383634313639383636653861666164393263306339616163383965643366
34656538383934313136316231643934663963643737353637633032313930623266323466653032
36636339373034336463313862353638313538316465373132616166323863373832626330383937
30313565373731663663336466363239303734336632396636346139636335646162353166643330
30323530613736613736623238393233316635333437333938313932623834643363616239343032
34626636323461303438376635633637636232386333636139626565303635656630343062353639
39396264386638326666303438623334336163343938336566353034656638323633343837376433
65366232303838643832623332663435636530363638636433303336356531633131356266396334
37313864393761623333653638383766366261353334653066316563386437383432343964616535
30306134383166666630636666316630383030346130353865386539303537363539636161643733
62343530396563393464656566363739313666613762613565386331366365393338313237636231
65326630313630363139626161373730373962656134363733633530376261613239323631623433
61396631333736383731363135383664663632643939376334633966373231363430326663323061
31666134396439346166346430363739626665663133323930363663303164616337316233656333
39303833653366353931343936623362346163363766346461653264353733363439643439336435
32656665623532666465663834363564666634653038616361393665303766326436306533613439
64616334393539643662303036626131623137383164303566623430646337353264343030303935
33313164643439643662393632323433366238306261623335656430396437396535653335343965
31616265363437356263616539343232333539393133323334633730366263386135393864393162
33653161623735363335323838353231326166376365656661373965353433336466623134386230
61653733393737643832303562663933353935626437316538393133613063316566646565323034
32643235303038656362663561666539633432383636333463396162353035323531623133656563
65376362643432643933393063393266663563663636616261663638626238653633303630383062
35363134323131323236623563353035306430343534643962333530383836666565646163346139
65633239396437346432346233643463333831663636323833386338386430373430336536313463
31386131653831623832346564343137326163326261306463666563643839653864353764353861
61663539646431633531653465333336663733663264336565386662626335376231643563356337
65653439613164306639353862656264333733386235333732643730623639333937396236616466
36303332626163323932356338313763363563326139633836343637663832633262323065326435
31643134303737316635336534613038313362396335636164656436623764656537303732643730
35613936303762623765656265663362613736636531633335666230643335633161303738663465
61636165376538313839646133653262343530393837313465343330626362626164366165323831
34396134616138363562313934626239353135336335656331383035393763306166666533643465
34383436356162323631373164373565313563656664663762343630383161646633356434643631
64306239373363343161663030343734366239356237393033643931646434393237363239333631
66343963633266613138353433383338343737636238346433366361643436363233373034656231
37633030623232613030633832306337663336616632333730373639323261646432616531366634
63663837623235383438363533313330663135323661626236333761323165663638623930653631
37633933383562396331326264623866353966633932663961643032383363613333323937383538
34653536313866336530343832336564353036376234383538323732306565636234623832376562
64626166366164626436663564353539323765663365303332373138373034333064626164336439
64333639313832656439613532326330336630613131386463663934653539663763646138343435
37636161623363613039666334626164643464313439376330653837363661393431376632326137
34383134386563343337363037616431366239623461356563613039393936653330303830303534
30373463363265653439633736396664623661646237633537373465316362383965333738333831
37373735636362376265376363386566313663343961623064343363366339373236316434653737
35356436316230626631623130386631636664623762613438313632613134396131336534393931
61663639653166376465323162313064623836623366393464386365313739626636303265343463
30336665636238343930303539313930626439326462343865326531616239323036306230363634
33626531393332393332663137303938613163653637376264663863643163346635616564326337
65393335383534323038656464353963316164396263663533643464623561343337623661663635
39663331633239336432366466623666313036393661623966396630623431626465646437363634
31623536346231373534656262336466336465353261663433363134633030393533326539666661
39623931313339646134363337343233346334366262646638613039363730613433313863396564
64643436653637356563393131666465663332326665373937643764376231336331616361323530
35303038323739316265363863316139363033386538346139636262626633303563353737363739
35656463646364323964366533656339643466323965323661306166393933353237643834373264
66623533623837306238393234666331636566616535323231393364643039323064343433393533
66653333633931303761376162666530386161336534343932633933616139306430326430326566
63656534343362323830643831643836303331643364396566626662333439646531616465623963
64616361396635393339643035616536643435353738383437303832626135316631633166613434
64366335373365653763353436333961663265643739646536343564383062396337333362363235
38326236363564313437343832383961303032643831363563646664373338616363623935656538
35376233326434623339376463346338636162306461656434626135623263613632393135306531
38326434636637366633336337663739633232386531336162333939333132336362363430363965
64316337353637656439343233343362666130663165613335633363353339633537323436323334
61343838383365346466336332643065353836366463613736353231343665623430373232333133
34616261653862353062376231356438363038643238333833353464653138303734373735303135
37663338363737666461646161653434636237313332343838346335313363616134653262633362
33653236343930316231636534613666373061373332653434356530343334313434366137636364
64393035383635646363373732636166383931343362356666643836313065346365396162393237
64623064643831363161653034316162363935356563316536393531643164643466323432323139
63643533373431653435383136663665316131343632383366396366376237346265303634326232
37653331666434316263653531323861386339326130396563663864373364663831336465326665
35383534633433356134636632353333346633383335383039626432323932333433303663383364
34633133323166313032393031346536333863306163333462623063663239666563653365643631
33623238343335333932303836396662323832366139616334653639653838303662313162643335
63396164353038636432653332376536323366343765383034366633666639353936316436613130
33633763396366363565636532613738663963363932373061313635376338626665616131346532
65323564333465616664393965633761653962616137663336333430663433646562333364633361
31613736366462333131356131303063386633386664366166653136333266663161663332353737
31623139663130363735336233353135323166613964653839313137316239393431306239343137
65643431353936616433336263353439383763663833333031653033353464653762656433393436
32376666306637636134

View File

@@ -0,0 +1,18 @@
---
# file: group_vars/ntp_servers/vars
#Ansible vars template for NTP servers.
ntp_servers:
- 0.us.pool.ntp.org
- 1.us.pool.ntp.org
allowed_networks:
- "192.168.1.0/24"
- "192.168.2.0/24"
- "192.168.3.0/24"
- "192.168.5.0/24"
- "192.168.9.0/24"
- "192.168.10.0/24"
- "192.168.250.0/24"
- "10.1.71.0/24"
- "10.1.82.0/24"

View File

@@ -0,0 +1,19 @@
---
# ansible/group_vars/proxmox/oidc.yml
#
# Proxmox OIDC configuration for Authentik integration.
# Client credentials come from vault.yml.
proxmox_oidc_realm_name: "authentik"
proxmox_oidc_issuer_url: "https://authentik.local.mk-labs.cloud/application/o/proxmox/"
proxmox_oidc_username_claim: "username"
proxmox_oidc_scopes: "openid email profile"
proxmox_oidc_autocreate: true
proxmox_oidc_default_realm: false
proxmox_oidc_comment: "Authentik SSO"
# ACL entries - grant your Authentik user admin access
proxmox_oidc_acl_entries:
- path: "/"
user: "rblundon@authentik"
role: "Administrator"

View File

@@ -0,0 +1,17 @@
$ANSIBLE_VAULT;1.1;AES256
64613361376364346139313833613465663361336634326430393261366630306466363935613139
3634363235343736643230623865386436333734663531310a343965396534336262356234623966
39353332626662636666383935383530613139626439373664323063633063316264383331646533
3366333837613731310a656462633638376432326365343135373863313665346566383933353630
37303565323630633739396433323165326262363863386233343134636236646366633765616432
33633165613239653762343935386637393437386431383961306436373765343736313634333933
30376463646464303066613561613564353938306664373464333966383664383034616439343735
38363966323366343465393530383736393364373361326234356365363163356632393834393464
39393530356235383830323065636535383138353864373237333164323436623737383435313261
64366362626232386438376131633533633162356432613835656465623762633464353936303236
63353633633262633461353963653534663336396134373435303266386162393965343165303335
65313436666334383965373837376664323861376162373834363439653539383231346634633737
30626431373739633930313937643938343165383536373465613535366562313439396533643864
36323032316131333962373732306334373939333363386564653465303261393337333466316130
32326533353165393635393130396164636363623937313566343333386562616662383038613565
35386638383365633362

View File

@@ -0,0 +1,59 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
proxmox_clone_node: "fantasyland"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu
# - 24.04
# - Fedora
# - 42
vm_os_distribution: "ubuntu"
vm_os_version: "24.04"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "large"
# Proxmox storage target.
vm_storage: "liberty-tree"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.111
# vm_mac_address: 'BC:24:11:11:BC:58'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn

View File

@@ -0,0 +1,25 @@
---
# ------------------------------------------------------------------------------
# FILE: ansible/host_vars/astro_orbiter/vars.yml
# HOST: astro-orbiter (10.1.71.130)
# ROLE: Ollama inference host with AMD RX 5700 GPU passthrough
# ------------------------------------------------------------------------------
ansible_host: 10.1.71.130
ansible_user: wed
ansible_become: true
# LVM root expansion — xlarge template uses sda3 partition, standard VG/LV names
common_expand_root_lvm: true
common_root_pv: /dev/sda3
common_root_vg: ubuntu-vg
common_root_lv: ubuntu-lv
# Ollama — all defaults apply; explicitly documented here for visibility
ollama_rocm_version: "6.2"
ollama_default_model: "qwen3:8b"
ollama_hsa_override_gfx_version: "10.1.0"
ollama_data_disk: /dev/sdb
ollama_data_vg: ollama-vg
ollama_data_lv: ollama-lv
ollama_data_dir: /var/lib/ollama

View File

@@ -0,0 +1,58 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
proxmox_clone_node: "fantasyland"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu
# - 24.04
# - Fedora
# - 42
vm_os_distribution: "ubuntu"
vm_os_version: "24.04"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "large-plus"
# Proxmox storage target.
vm_storage: "general"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.52
vm_mac_address: 'BC:24:11:11:BC:58'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn

View File

@@ -0,0 +1,99 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu (clone)
# - 24.04
# - Fedora (clone)
# - 42
# - Talos (create)
# - 1.11.5
vm_os_distribution: "talos"
vm_os_version: "1.11.5"
# VM ISO
# Talos: talos-v1.11.5-nocloud-amd64.iso
vm_iso_storage: "templates"
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
# Comment out for VM create
proxmox_clone_node: "fantasyland"
# Proxmox storage target.
vm_storage: "liberty-tree"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "large-plus"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.52
vm_mac_address: 'BC:24:11:11:BC:58'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
# Dictionaries for VM resources
# - cores (quantity)
# - memory (MB)
# - virtual disk (GiB)
vm_definitions:
small:
cores: 2
memory: 2048
disk: 8
medium:
cores: 2
memory: 4096
disk: 16
large:
cores: 4
memory: 4096
disk: 32
large-plus:
cores: 4
memory: 4096
disk: 48
xlarge:
cores: 4
memory: 8192
disk: 64
xlarge-plus:
cores: 4
memory: 8192
disk: 128

View File

@@ -0,0 +1,16 @@
---
# ------------------------------------------------------------------------------
# FILE: ansible/host_vars/astro_orbiter/vars.yml
# HOST: astro-orbiter (10.1.71.130)
# ROLE: Ollama inference host with AMD RX 5700 GPU passthrough
# ------------------------------------------------------------------------------
ansible_host: 10.1.71.131
ansible_user: wed
ansible_become: true
# LVM root expansion — xlarge template uses sda3 partition, standard VG/LV names
common_expand_root_lvm: true
common_root_pv: /dev/sda3
common_root_vg: ubuntu-vg
common_root_lv: ubuntu-lv

View File

@@ -0,0 +1,48 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
proxmox_clone_node: "fantasyland"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Fedora (42)
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
vm_clone_source: "fedora-42-small"
# Proxmox storage target.
vm_storage: "mk-general"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.21
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn

View File

@@ -1,9 +1,9 @@
--- ---
# file: host_vars/matchbox/vars # file: host_vars/docker01/vars
# Networking # Networking
# primary_interface: "enp1s0f0" # primary_interface: "enp1s0f0"
mac_address: "BC:24:11:03:A9:7B" #mac_address: "BC:24:11:03:A9:7B"
ip_address: 10.1.71.211 ip_address: 10.1.71.211
hostname: "{{ inventory_hostname }}.{{ base_domain }}" hostname: "{{ inventory_hostname }}.{{ base_domain }}"

View File

@@ -0,0 +1,12 @@
---
# file: host_vars/guest-relations/vars
# guest-relations — Guest Relations
# VM provisioned by Terraform (pre-pipeline bootstrap)
# ─── Network ─────────────────────────────────────────────────────────────────
ip_address: 10.1.71.40
# ─── Application ─────────────────────────────────────────────────────────────
app_role: identity_provider
app_name: authentik
app_deployment: docker_compose

View File

@@ -0,0 +1,60 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
proxmox_clone_node: "fantasyland"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu
# - 24.04
# - Fedora
# - 42
vm_os_distribution: "ubuntu"
vm_os_version: "24.04"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "xlarge"
# Proxmox storage target.
vm_storage: "general"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.61
vm_mac_address: 'BC:24:11:C9:3A:13'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn

View File

@@ -0,0 +1,99 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu (clone)
# - 24.04
# - Fedora (clone)
# - 42
# - Talos (create)
# - 1.11.5
vm_os_distribution: "talos"
vm_os_version: "1.11.5"
# VM ISO
# Talos: talos-v1.11.5-nocloud-amd64.iso
vm_iso_storage: "templates"
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
# Comment out for VM create
proxmox_clone_node: "fantasyland"
# Proxmox storage target.
vm_storage: "liberty-tree"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "xlarge"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.61
vm_mac_address: 'BC:24:11:C9:3A:13'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
# Dictionaries for VM resources
# - cores (quantity)
# - memory (MB)
# - virtual disk (GiB)
vm_definitions:
small:
cores: 2
memory: 2048
disk: 8
medium:
cores: 2
memory: 4096
disk: 16
large:
cores: 4
memory: 4096
disk: 32
large-plus:
cores: 4
memory: 4096
disk: 48
xlarge:
cores: 4
memory: 8192
disk: 64
xlarge-plus:
cores: 4
memory: 8192
disk: 128

View File

@@ -0,0 +1,12 @@
---
# file: host_vars/lightning_lane/vars
# lightning-lane — Traefik Reverse Proxy / Load Balancer
# VM provisioned by Terraform (pre-pipeline bootstrap)
# ─── Network ─────────────────────────────────────────────────────────────────
ip_address: 10.1.71.35
# ─── Application ─────────────────────────────────────────────────────────────
app_role: reverse_proxy
app_name: traefik
app_deployment: docker_compose

View File

@@ -0,0 +1,16 @@
---
# Host-specific vars for main-street-station (JMRI headless server)
# LCRR - Lake Country Railroad, Milwaukee Road Oct 1956, HO scale
# JMRI profile ID — find with: ls ~/.jmri/profiles/ on the old box
# Format: <name>.<8-char-hex> e.g. LCRR.3d3f1dfc
# TODO: fill in after restoring config from GitHub backup
jmri_profile_id: ""
# USB serial device for NCE command station
# Verify after install: ls -la /dev/ttyUSB* /dev/ttyACM*
jmri_serial_device: /dev/ttyUSB0
# Path to JMRI config backup for restore task (leave empty to skip)
# Point at a local checkout of the LCRR GitHub repo
jmri_config_src: ""

View File

@@ -0,0 +1,7 @@
---
# main-street-station — JMRI / LCRR server
jmri_profile_id: "My_JMRI_Railroad.3f178885"
jmri_lcrr_repo: "ssh://git@gitea.mk-labs.cloud:2221/rblundon/LCRR.git"
jmri_leviton_email: "{{ leviton_email }}"
jmri_leviton_password: "{{ leviton_password }}"
jmri_ssh_authorized_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINnSM/9fO8rz/amqkyoGUzUKNNzzmtSXPwOCr1O9zKNO ansible"

View File

@@ -0,0 +1,59 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
proxmox_clone_node: "fantasyland"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu
# - 24.04
# - Fedora
# - 42
vm_os_distribution: "ubuntu"
vm_os_version: "24.04"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "xlarge"
# Proxmox storage target.
vm_storage: "general"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.62
vm_mac_address: 'BC:24:11:AA:8F:3A'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn

View File

@@ -0,0 +1,99 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu (clone)
# - 24.04
# - Fedora (clone)
# - 42
# - Talos (create)
# - 1.11.5
vm_os_distribution: "talos"
vm_os_version: "1.11.5"
# VM ISO
# Talos: talos-v1.11.5-nocloud-amd64.iso
vm_iso_storage: "templates"
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
# Comment out for VM create
proxmox_clone_node: "fantasyland"
# Proxmox storage target.
vm_storage: "liberty-tree"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "xlarge"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.62
vm_mac_address: 'BC:24:11:AA:8F:3A'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
# Dictionaries for VM resources
# - cores (quantity)
# - memory (MB)
# - virtual disk (GiB)
vm_definitions:
small:
cores: 2
memory: 2048
disk: 8
medium:
cores: 2
memory: 4096
disk: 16
large:
cores: 4
memory: 4096
disk: 32
large-plus:
cores: 4
memory: 4096
disk: 48
xlarge:
cores: 4
memory: 8192
disk: 64
xlarge-plus:
cores: 4
memory: 8192
disk: 128

View File

@@ -0,0 +1,59 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
proxmox_clone_node: "fantasyland"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu
# - 24.04
# - Fedora
# - 42
vm_os_distribution: "ubuntu"
vm_os_version: "24.04"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "large-plus"
# Proxmox storage target.
vm_storage: "general"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.51
vm_mac_address: 'BC:24:11:1A:E8:8C'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn

View File

@@ -0,0 +1,99 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu (clone)
# - 24.04
# - Fedora (clone)
# - 42
# - Talos (create)
# - 1.11.5
vm_os_distribution: "talos"
vm_os_version: "1.11.5"
# VM ISO
# Talos: talos-v1.11.5-nocloud-amd64.iso
vm_iso_storage: "templates"
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
# Comment out for VM create
proxmox_clone_node: "fantasyland"
# Proxmox storage target.
vm_storage: "liberty-tree"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "large-plus"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.51
vm_mac_address: 'BC:24:11:1A:E8:8C'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
# Dictionaries for VM resources
# - cores (quantity)
# - memory (MB)
# - virtual disk (GiB)
vm_definitions:
small:
cores: 2
memory: 2048
disk: 8
medium:
cores: 2
memory: 4096
disk: 16
large:
cores: 4
memory: 4096
disk: 32
large-plus:
cores: 4
memory: 4096
disk: 48
xlarge:
cores: 4
memory: 8192
disk: 64
xlarge-plus:
cores: 4
memory: 8192
disk: 128

View File

@@ -0,0 +1,59 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
proxmox_clone_node: "fantasyland"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu
# - 24.04
# - Fedora
# - 42
vm_os_distribution: "ubuntu"
vm_os_version: "24.04"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "large-plus"
# Proxmox storage target.
vm_storage: "general"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.53
vm_mac_address: 'BC:24:11:84:D7:2F'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn

View File

@@ -0,0 +1,99 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu (clone)
# - 24.04
# - Fedora (clone)
# - 42
# - Talos (create)
# - 1.11.5
vm_os_distribution: "talos"
vm_os_version: "1.11.5"
# VM ISO
# Talos: talos-v1.11.5-nocloud-amd64.iso
vm_iso_storage: "templates"
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
# Comment out for VM create
proxmox_clone_node: "fantasyland"
# Proxmox storage target.
vm_storage: "liberty-tree"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "large-plus"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.53
vm_mac_address: 'BC:24:11:84:D7:2F'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
# Dictionaries for VM resources
# - cores (quantity)
# - memory (MB)
# - virtual disk (GiB)
vm_definitions:
small:
cores: 2
memory: 2048
disk: 8
medium:
cores: 2
memory: 4096
disk: 16
large:
cores: 4
memory: 4096
disk: 32
large-plus:
cores: 4
memory: 4096
disk: 48
xlarge:
cores: 4
memory: 8192
disk: 64
xlarge-plus:
cores: 4
memory: 8192
disk: 128

View File

@@ -0,0 +1,100 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts creation via clone or create.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu (clone)
# - 24.04
# - Fedora (clone)
# - 42
# - Talos (create)
# - 1.11.5
vm_os_distribution: "talos"
vm_os_version: "1.11.5"
# VM ISO
# Talos: talos-v1.11.5-nocloud-amd64.iso
vm_iso_storage: "templates"
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
# Comment out for VM create
proxmox_clone_node: "fantasyland"
# Proxmox storage target.
vm_storage: "liberty-tree"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "small"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "tomorrowland"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
# Comment out vm_mac_address if new MAC address should be generated
ip_address: 10.1.71.99
vm_mac_address: 'BC:24:11:97:C3:AA'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
# Dictionaries for VM resources
# - cores (quantity)
# - memory (MB)
# - virtual disk (GiB)
vm_definitions:
small:
cores: 2
memory: 2048
disk: 8
medium:
cores: 2
memory: 4096
disk: 16
large:
cores: 4
memory: 4096
disk: 32
large-plus:
cores: 4
memory: 4096
disk: 48
xlarge:
cores: 4
memory: 8192
disk: 64
xlarge-plus:
cores: 4
memory: 8192
disk: 128

View File

@@ -0,0 +1,50 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
proxmox_clone_node: "fantasyland"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Fedora (42)
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_clone_source: "fedora-42-small"
# Proxmox storage target.
vm_storage: "general"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "fantasyland"
proxmox_host_target: "fantasyland"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.21
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn

View File

@@ -0,0 +1,59 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts created via cloning.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
proxmox_clone_node: "fantasyland"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu
# - 24.04
# - Fedora
# - 42
vm_os_distribution: "ubuntu"
vm_os_version: "24.04"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "large"
# Proxmox storage target.
vm_storage: "general"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "pve03"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
ip_address: 10.1.71.23
# vm_mac_address: 'BC:24:11:11:BC:58'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn

View File

@@ -0,0 +1,27 @@
---
# file: host_vars/turnstile/vars
# turnstile — Smallstep step-ca SSH Certificate Authority
# VM provisioned by Terraform (pre-pipeline bootstrap)
# ─── Network ─────────────────────────────────────────────────────────────────
ip_address: 10.1.71.34
# ─── Application ─────────────────────────────────────────────────────────────
app_role: ssh_certificate_authority
app_name: step-ca
app_deployment: docker_compose
# ─── step-ca Configuration ───────────────────────────────────────────────────
stepca_hostname: turnstile.local.mk-labs.cloud
stepca_dns_names: "turnstile.local.mk-labs.cloud,10.1.71.34"
stepca_ssh_enabled: true
stepca_listen_port: 9000
# ─── OIDC Provisioner (Authentik) ────────────────────────────────────────────
# Client ID and secret stored in vault
stepca_oidc_provisioner_name: authentik
stepca_oidc_configuration_endpoint: "https://authentik.local.mk-labs.cloud/application/o/step-ca/.well-known/openid-configuration"
stepca_oidc_listen_address: ":10000"
stepca_oidc_domains:
- "local.mk-labs.cloud"
- "protonmail.com"

View File

@@ -0,0 +1,17 @@
$ANSIBLE_VAULT;1.1;AES256
30383930323363386234333433636465393263613336646464666365643730386430353864616334
3865343031653162313736656437373666636136653263360a613961646133376262633164393535
31363434633634303035663133316230633538363936303266373931313661346563333832653032
6239376363646530620a303231363333363233623038326165316463383662656565626534396232
62333133623530643932643430663166373930353733363866336533643233373261333130613635
31633337376337663833613634666436383862386431636537373363343333323932363761653366
61353166613162346436396138316561646165303566376366323462663861616364336539313138
30386430373566353761383636626335393463376661356666303861353564313832346333396134
30653861363866336331336164323130623230666237356165613934333239386536373664643065
33643139346562346663313533643433353462363665323166313364373335366665373435643935
65303863663864393238393732303065343364306264396333376233666233346664323334336532
38373164383835663163363137643531303163396236623565666436363261393563343133333161
36663934303633663062346161636333396135336135616136303664636562363862353764343562
39643430396636313139336130656363306562363430346233313530663963366238326334623135
62383632613061316262323038303333323739303137363334626637666663666263613132336433
65346266393733633632

View File

@@ -0,0 +1,104 @@
---
# file: host_vars/vm_template/vars
# Ansible vars template for hosts creation via clone or create.
# Supported hypervisors:
# - Proxmox
platform: "proxmox"
# Templates are named in the following format (all lower case): <OS Distribution>-<OS Version>-<VM Size>
# Current OS offerings are:
# - Ubuntu (clone)
# - 24.04
# - Fedora (clone)
# - 42
# - Talos (create)
# - 1.11.5
vm_os_distribution: "ubuntu"
vm_os_version: "24.04"
# VM ISO
# Talos: talos-v1.11.5-nocloud-amd64.iso
vm_iso_storage: "templates"
vm_iso: "talos-v{{ vm_os_version }}-nocloud-amd64.iso"
# This is the Proxmox node where all the VM templates are stored. (Templates are not global.)
# Comment out for VM create
proxmox_clone_node: "fantasyland"
# Proxmox storage target.
vm_storage: "general"
# Current VM sizes are:
# - Small: 2 cores, 2GB memory, 8 GiB virtual disk
# - Medium: 2 cores, 4GB memory, 16 GiB virtual disk
# - Large: 4 cores, 4GB memory, 32 GiB virtual disk
# - Large Plus: 4 cores, 4GB memory, 48 GiB virtual disk
# - Xlarge: 4 cores, 8GB memory, 64 GiB virtual disk
# - Xlarge Plus: 4 cores, 8GB memory, 128 GiB virtual disk
vm_size: "large-plus"
# Proxmox does not yet do dynamic load balancing, the host target sets the target for HA groups
# and backup groups. (ha_group will be factored out in the next functionality update.)
ha_group: "pve03"
proxmox_host_target: "tomorrowland"
# Currently, only single NIC VMs using IPv4 are supported via cloning. The IP address also
# sets the Proxmox VMID. The VMID is a combination of the 3rd and 4th octet of the IPv4 address.
# Comment out vm_mac_address if new MAC address should be generated
ip_address: 10.1.71.249
# vm_mac_address: 'BC:24:11:11:BC:58'
# Software
# Future enhancement will allow specification of additional software to automatically deploy to
# the VM after creation.
#terraform_version: "1.11.3"
# ---
vm_clone_source: "{{ vm_os_distribution }}-{{ vm_os_version }}-{{ vm_size }}"
hostname: "{{ inventory_hostname }}.{{ base_domain }}" # Change variable to fqdn
# Dictionaries for VM resources
# - cores (quantity)
# - memory (MB)
# - virtual disk (GiB)
small:
cores: 2
memory: 2048
disk: 8
medium:
cores: 2
memory: 4096
disk: 16
large:
cores: 4
memory: 4096
disk: 32
large-plus:
cores: 4
memory: 4096
disk: 48
xlarge:
cores: 4
memory: 8192
disk: 64
xlarge-plus:
cores: 4
memory: 8192
disk: 128

125
ansible/inventory.yml Executable file
View File

@@ -0,0 +1,125 @@
# file: inventory.yml
proxmox:
hosts:
main-street-usa:
ansible_host: 10.1.71.11
ansible_user: wed
ansible_become: true
tomorrowland:
fantasyland:
magic_kingdom:
hosts:
main-street-usa:
tomorrowland:
fantasyland:
dns_server:
hosts:
monorail:
ntp_servers:
hosts:
sundial:
load_balancers:
hosts:
lightning-lane:
ansible_become: true
step_ca_server:
hosts:
turnstile:
ansible_become: true
authentik_server:
hosts:
guest-relations:
ansible_become: true
gitea_servers:
hosts:
mad-tea-party:
ansible_user: wed
ansible_become: true
nextcloud_server:
hosts:
the-grid:
ansible_become: true
semaphore_server:
hosts:
figment:
ansible_host: 10.1.71.37
ansible_user: wed
ansible_become: true
n8n_server:
hosts:
tiki-room:
ollama_server:
hosts:
astro-orbiter:
ansible_host: 10.1.71.130
ansible_user: wed
ansible_become: true
hermes_server:
hosts:
carousel-of-progress:
ansible_host: 10.1.71.131
ansible_user: wed
ansible_become: true
honcho_server:
hosts:
lincoln:
ansible_host: 10.1.71.132
ansible_user: wed
ansible_become: true
jmri_server:
hosts:
main-street-station:
ansible_host: 192.168.10.40
ansible_user: wed
ansible_become: true
papermc_server:
# ansible-galaxy role install engonzal.papermc
hosts:
arcade:
dev_servers:
hosts:
scrim:
backstage:
ansible_host: 10.1.71.133
ansible_user: wed
ansible_become: true
# dhcp_server:
# hosts:
# matchbox:
backup_servers:
hosts:
timekeeper:
talos_control:
hosts:
city-hall:
ansible_become: true
vars:
talosctl_version: "v1.12.4"
talos_cluster_name: "fastpass"
# matchbox_server:
# hosts:
# matchbox:
terraform_server:
hosts:
infra01:

5
ansible/mk Executable file
View File

@@ -0,0 +1,5 @@
space-mountain
splash-mountain
big-thunder-mountain
peter-pans-flight
haunted-mansion

View File

@@ -0,0 +1,84 @@
---
- name: Bind VM MAC address to IP address on Ubiquiti
hosts: all
gather_facts: false
tasks:
- name: Set VM MAC address (if defined in vars)
delegate_to: "localhost"
community.proxmox.proxmox_kvm:
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password }}"
api_host: "{{ proxmox_host }}"
node: "{{ proxmox_clone_node }}"
vmid: "{{ vm_id }}"
net:
net0: "virtio={{ vm_mac_address }},bridge=vmbr0,firewall=1"
update: true
update_unsafe: true
when: vm_mac_address is defined
- name: Get VM MAC address from Proxmox (not defined in vars)
when: vm_mac_address is not defined
block:
- name: Retrieve information about specific VM by name and get current configuration
delegate_to: "localhost"
community.proxmox.proxmox_vm_info:
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password }}"
api_host: "{{ proxmox_host }}"
name: "{{ inventory_hostname }}"
config: current
register: proxmox_vm_info
- name: Extract net0 information
ansible.builtin.set_fact:
vm_net0: "{{ proxmox_vm_info.proxmox_vms[0].config.net0 }}"
- name: Extract MAC address using regex
ansible.builtin.set_fact:
vm_mac_address: "{{ vm_net0 | regex_search('([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}') }}"
- name: Assign VM MAC address to IP address on Ubiquiti with Terraform
when: vm_mac_address is defined
delegate_to: "{{ groups['terraform_server'][0] }}"
block:
- name: Create a directory if it does not exist
ansible.builtin.file:
path: "~/homelab/terraform/unifi-dhcp"
state: directory
mode: '0755'
- name: Create Unifi provider file from template
ansible.builtin.template:
src: templates/unifi_provider.tf.j2
dest: ~/homelab/terraform/unifi-dhcp/provider.tf
mode: '0644'
- name: Create Unifi user (host) file from template
ansible.builtin.template:
src: templates/unifi-user.tf.j2
dest: ~/homelab/terraform/unifi-dhcp/{{ inventory_hostname }}.tf
mode: '0644'
- name: Configure Unifi via Terraform
community.general.terraform:
project_path: ~/homelab/terraform/unifi-dhcp
state: present
force_init: true
# - name: Add line to hosts file
# delegate_to: "{{ groups['dhcp_server'][0] }}"
# become: true
# ansible.builtin.lineinfile:
# path: /etc/dnsmasq.d/hosts.conf
# regexp: "# {{ inventory_hostname }}$"
# line: "dhcp-host={{ vm_mac_address | lower }},{{ ip_address }} # {{ inventory_hostname }}"
# state: present
# # restart dnsmasq service
# - name: Restart service dnsmasq
# delegate_to: "{{ groups['dhcp_server'][0] }}"
# become: true
# ansible.builtin.service:
# name: dnsmasq
# state: restarted

View File

@@ -0,0 +1,13 @@
---
# Replacement for add_technitium_dns_entry.yml
# This playbook uses the modular task file approach
- name: Add DNS entry using Technitium DNS
hosts: all
gather_facts: false
tasks:
- name: Include Technitium DNS entry task
ansible.builtin.include_tasks: tasks/add_technitium_dns_entry.yml
vars:
host_name: "{{ inventory_hostname }}"

View File

@@ -0,0 +1,95 @@
---
# ------------------------------------------------------------------------------
# FILE: playbooks/add_service_route.yml
# DESCRIPTION: Ensures all services in the Traefik dynamic config directory
# are routed and have DNS CNAME records on monorail.
#
# 1. Syncs boilerplates/traefik/dynamic/ to lightning-lane
# 2. Scans the directory for service configs
# 3. Extracts all hostnames from Host() rules (supports multi-host)
# 4. Creates CNAME records for each hostname -> lightning-lane
#
# PREREQUISITES:
# - Service dynamic config YAML committed to boilerplates/traefik/dynamic/
# - vault_technitium_api_key defined in group_vars/all/vault
#
# USAGE:
# ansible-playbook -i inventory.yml playbooks/add_service_route.yml
# ------------------------------------------------------------------------------
- name: Sync Traefik routes and ensure DNS records
hosts: localhost
connection: local
gather_facts: false
vars:
base_domain: "local.mk-labs.cloud"
dns_server: "monorail"
traefik_host: "lightning-lane.local.mk-labs.cloud"
traefik_user: "wed"
traefik_dynamic_path: "/opt/docker/traefik/dynamic/"
dynamic_config_dir: "{{ playbook_dir }}/../../boilerplates/traefik/dynamic"
# Files in the dynamic directory that are NOT service routes
exclude_configs:
- default.yml
tasks:
# ── Step 1: Sync dynamic config to lightning-lane ──
- name: Sync Traefik dynamic configuration to lightning-lane
ansible.builtin.shell: >
rsync -av --delete
{{ dynamic_config_dir }}/
{{ traefik_user }}@{{ traefik_host }}:{{ traefik_dynamic_path }}
register: sync_result
changed_when: "'sending incremental file list' in sync_result.stdout"
# ── Step 2: Discover hostnames from Traefik router rules ──
- name: Find all dynamic config files
ansible.builtin.find:
paths: "{{ dynamic_config_dir }}"
patterns: "*.yml"
register: config_files
- name: Read config files
ansible.builtin.slurp:
src: "{{ item.path }}"
register: slurped_configs
loop: "{{ config_files.files }}"
when: item.path | basename not in exclude_configs
- name: Extract all hostnames from Host() rules
ansible.builtin.set_fact:
hostnames: >-
{% set hosts = [] -%}
{% for result in slurped_configs.results if result.content is defined -%}
{% set content = result.content | b64decode -%}
{% for match in content | regex_findall('Host\(`([^`]+)`\)') -%}
{% for h in match.split(' || ') -%}
{% set h = h | regex_replace('`', '') | trim -%}
{% if h.endswith('.local.mk-labs.cloud') and h not in hosts -%}
{% set _ = hosts.append(h) -%}
{% endif -%}
{% endfor -%}
{% endfor -%}
{% endfor -%}
{{ hosts | unique | list }}
- name: Display hostnames to create
ansible.builtin.debug:
msg: "Hostnames found: {{ hostnames }}"
# ── Step 3: Create DNS CNAME records ──
- name: Create DNS CNAME record for each hostname
effectivelywild.technitium_dns.technitium_dns_add_record:
api_url: "http://{{ dns_server }}.{{ base_domain }}"
api_token: "{{ vault_technitium_api_key }}"
zone: "{{ base_domain }}"
name: "{{ item }}"
type: "CNAME"
cname: "lightning-lane.{{ base_domain }}"
ttl: 360
validate_certs: false
loop: "{{ hostnames }}"
loop_control:
label: "{{ item }}"

View File

@@ -0,0 +1,18 @@
---
- name: Add entry to Technitium DNS
hosts: all
gather_facts: false
tasks:
- name: "Create DNS entry for {{ inventory_hostname }}"
delegate_to: localhost
effectivelywild.technitium_dns.technitium_dns_add_record:
api_url: "http://{{ dns_server }}.{{ base_domain }}"
api_token: "{{ vault_technitium_api_key }}"
zone: "{{ base_domain }}"
name: "{{ inventory_hostname }}.{{ base_domain }}"
type: "A"
ipAddress: "{{ ip_address }}"
ptr: true
ttl: 360
# validate_certs: false

View File

@@ -0,0 +1,20 @@
---
# ansible/playbooks/configure_proxmox_oidc.yml
#
# Configures Proxmox OIDC authentication with Authentik.
# Only targets one node since realm config is cluster-wide.
#
# Usage:
# cd ansible
# ansible-playbook -i inventory.yml playbooks/configure_proxmox_oidc.yml
#
# To also set up ACL entries for your user:
# ansible-playbook -i inventory.yml playbooks/configure_proxmox_oidc.yml \
# -e '{"proxmox_oidc_acl_entries": [{"path": "/", "user": "rblundon@authentik", "role": "Administrator"}]}'
- name: Configure Proxmox Authentik OIDC
hosts: main-street-usa
become: true
roles:
- role: proxmox
tags: [proxmox-oidc]

View File

@@ -0,0 +1,37 @@
---
- name: Master playbook to install and configure unbound
hosts: unbound_servers
become: true
tasks:
- name: Permit traffic in default zone for dns service
ansible.posix.firewalld:
service: dns
permanent: true
immediate: true
state: enabled
- name: Create the directory
ansible.builtin.file:
path: /etc/systemd/resolved.conf.d
state: directory
mode: '0755'
owner: root
group: root
- name: Put `unbound.conf` in the correct place
ansible.builtin.copy:
src: ../roles/common/files/unbound.conf
dest: /etc/systemd/resolved.conf.d/unbound.conf
mode: '0644'
owner: root
group: root
- name: Restart service systemd-resolved
ansible.builtin.service:
name: systemd-resolved
state: restarted
- name: Install unbound via role
ansible.builtin.import_role:
name: Anthony25.unbound

23
ansible/playbooks/create_vm.yml Executable file
View File

@@ -0,0 +1,23 @@
---
- name: Master playbook to create VM
hosts: all
gather_facts: false
- name: Proxmox Create VM Playbook
ansible.builtin.import_playbook: proxmox_create_vm.yml
- name: Set cloud-init network
ansible.builtin.import_playbook: populate_cloud_init.yml
when: vm_os_distribution == "ubuntu"
- name: DNS Playbook
ansible.builtin.import_playbook: add_dns_entry.yml
- name: DHCP Playbook
ansible.builtin.import_playbook: add_dhcp_reservation.yml
- name: Start VM Playbook
ansible.builtin.import_playbook: proxmox_start_vm.yml
- name: Set Hostname Playbook
ansible.builtin.import_playbook: set_hostname.yml

View File

@@ -6,11 +6,15 @@
- name: Proxmox Clone VM Playbook - name: Proxmox Clone VM Playbook
ansible.builtin.import_playbook: proxmox_clone_vm.yml ansible.builtin.import_playbook: proxmox_clone_vm.yml
- name: Set cloud-init network
ansible.builtin.import_playbook: populate_cloud_init.yml
when: vm_os_distribution == "ubuntu"
- name: DNS Playbook - name: DNS Playbook
ansible.builtin.import_playbook: add_dns_entry.yml ansible.builtin.import_playbook: add_dns_entry.yml
- name: DHCP Playbook - name: DHCP Playbook
ansible.builtin.import_playbook: add_dhcp_entry.yml ansible.builtin.import_playbook: add_dhcp_reservation.yml
- name: Start VM Playbook - name: Start VM Playbook
ansible.builtin.import_playbook: proxmox_start_vm.yml ansible.builtin.import_playbook: proxmox_start_vm.yml

View File

@@ -0,0 +1,6 @@
---
- name: Apply day0 baseline
hosts: "{{ target | default('all') }}"
become: true
roles:
- day0-baseline

View File

@@ -0,0 +1,32 @@
---
# ============================================================================
# day0_expand_root_lv.yml
# ----------------------------------------------------------------------------
# Reclaims unallocated PE on the root volume group, extending the root LV
# to fill the VG and resizing the underlying filesystem (ext4 or xfs).
#
# Belongs to the day0 host-provisioning lifecycle. The Ubuntu Server
# autoinstall template ships with the root LV at ~half the disk size by
# default; this playbook is the canonical one-shot fix-up for that.
#
# Idempotent and safe to re-run. Hosts without LVM are no-op'd cleanly.
#
# Opt-out: set `expand_root_lv_skip: true` in host_vars/<host>.yml for
# hosts where free PE should NOT be claimed by root (e.g. hosts with a
# planned second LV in the same VG for application data).
#
# Usage:
# ansible-playbook playbooks/day0_expand_root_lv.yml
# ansible-playbook playbooks/day0_expand_root_lv.yml -e target=lincoln
# ansible-playbook playbooks/day0_expand_root_lv.yml -e target=honcho_server
# ============================================================================
- name: Expand root logical volume to fill VG
hosts: "{{ target | default('all') }}"
become: true
gather_facts: true
tasks:
- name: Apply expand_root_lv role unless host opts out
ansible.builtin.include_role:
name: expand_root_lv
when: not (expand_root_lv_skip | default(false) | bool)

View File

@@ -0,0 +1,23 @@
---
# ============================================================================
# day0_linux_baseline.yml
# ----------------------------------------------------------------------------
# Applies the mk-labs Linux baseline (linux-baseline role) to one or more
# hosts. Idempotent and safe to re-run.
#
# Usage:
# ansible-playbook playbooks/day0_linux_baseline.yml
# ansible-playbook playbooks/day0_linux_baseline.yml -e target=figment
# ansible-playbook playbooks/day0_linux_baseline.yml -e target=semaphore_server
#
# To trigger an opt-in full system upgrade:
# ansible-playbook playbooks/day0_linux_baseline.yml \
# -e target=figment -e 'baseline_features={"full_upgrade": true}'
# ============================================================================
- name: Apply mk-labs Linux baseline
hosts: "{{ target | default('all') }}"
become: true
gather_facts: true
roles:
- linux-baseline

View File

@@ -0,0 +1,30 @@
---
# ============================================================================
# day0_provision.yml
# ----------------------------------------------------------------------------
# Umbrella day0 playbook. Runs the full host-provisioning lifecycle in
# the correct order against newly-built VMs, so the operator runs ONE
# command per new host rather than chaining day0 steps manually.
#
# Order matters:
# 1. linux-baseline — timezone, NTP, packages, SSH hardening, jarvis user
# 2. expand_root_lv — reclaim PE left unallocated by the Ubuntu
# autoinstall template default
#
# Idempotent: every step is safe to re-run. Suitable to apply periodically
# from Semaphore as a baseline-drift check.
#
# Usage:
# ansible-playbook playbooks/day0_provision.yml -e target=lincoln
# ansible-playbook playbooks/day0_provision.yml -e target=honcho_server
#
# For finer control over a single phase, the constituent playbooks are:
# playbooks/day0_linux_baseline.yml
# playbooks/day0_expand_root_lv.yml
# ============================================================================
- name: Import day0 linux baseline
ansible.builtin.import_playbook: day0_linux_baseline.yml
- name: Import day0 expand root LV
ansible.builtin.import_playbook: day0_expand_root_lv.yml

View File

@@ -0,0 +1,400 @@
---
# ansible/playbooks/day1_configure_netbox_catalog.yml
#
# Creates the tag taxonomy and custom fields in NetBox for service catalog
# documentation. Run once (idempotent — uses name-based checks).
#
# Usage (from ansible/ directory):
# ansible-playbook playbooks/day1_configure_netbox_catalog.yml
#
# Requires:
# - vault_netbox_token in Ansible Vault
# - netbox reachable at http://fire-station.local.mk-labs.cloud
- name: Configure NetBox service catalog taxonomy
hosts: localhost
gather_facts: false
vars_files:
- "{{ playbook_dir }}/../group_vars/all/vault"
vars:
netbox_url: "http://fire-station.local.mk-labs.cloud"
netbox_token: "{{ vault_netbox_token }}"
netbox_api: "{{ netbox_url }}/api"
headers:
Authorization: "Token {{ netbox_token }}"
Content-Type: "application/json"
Accept: "application/json"
# ── Tag definitions ───────────────────────────────────────────────────
tags:
# Infrastructure type
- name: k8s
slug: k8s
color: "2196f3" # blue
description: "Workload running in the fastpass Kubernetes cluster"
- name: vm
slug: vm
color: "4caf50" # green
description: "Traditional VM or LXC on Proxmox"
# Service tier
- name: platform
slug: platform
color: "9c27b0" # purple
description: "Platform/infrastructure service (not user-facing)"
- name: application
slug: application
color: "ff9800" # orange
description: "User-facing application workload"
# Service categories
- name: monitoring
slug: monitoring
color: "607d8b" # grey
description: "Metrics, logging, alerting"
- name: auth
slug: auth
color: "607d8b"
description: "Authentication and SSO"
- name: gitops
slug: gitops
color: "607d8b"
description: "GitOps and CI/CD"
- name: dashboard
slug: dashboard
color: "607d8b"
description: "Dashboard and portal services"
- name: storage
slug: storage
color: "607d8b"
description: "Storage and file services"
- name: dns
slug: dns
color: "607d8b"
description: "DNS and name resolution"
- name: automation
slug: automation
color: "607d8b"
description: "Automation and orchestration"
- name: networking
slug: networking
color: "607d8b"
description: "Network infrastructure services"
- name: inference
slug: inference
color: "607d8b"
description: "AI/ML inference workloads"
# ── Custom field definitions ──────────────────────────────────────────
# object_types use app_label.model format
custom_fields:
- name: hostnames
label: Hostnames
type: longtext
object_types:
- ipam.ipaddress
- virtualization.virtualmachine
description: "All DNS names that resolve to this service (comma-separated)"
ui_visible: always
ui_editable: yes
- name: namespace
label: Namespace
type: text
object_types:
- ipam.ipaddress
- virtualization.virtualmachine
description: "Kubernetes namespace (blank for VM-based services)"
ui_visible: always
ui_editable: yes
- name: managed_by
label: Managed By
type: select
object_types:
- ipam.ipaddress
- virtualization.virtualmachine
description: "How this service is managed"
choices:
- ArgoCD
- Ansible
- Manual
ui_visible: always
ui_editable: yes
- name: thematic_name
label: Thematic Name
type: text
object_types:
- ipam.ipaddress
- virtualization.virtualmachine
description: "Disney/Magic Kingdom thematic hostname for this service"
ui_visible: always
ui_editable: yes
tasks:
# ── Tags ─────────────────────────────────────────────────────────────
- name: Fetch existing tags
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/tags/?limit=200"
method: GET
headers: "{{ headers }}"
return_content: true
status_code: 200
register: existing_tags_response
- name: Set existing tag slugs fact
ansible.builtin.set_fact:
existing_tag_slugs: "{{ existing_tags_response.json.results | map(attribute='slug') | list }}"
- name: Create tags
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/tags/"
method: POST
headers: "{{ headers }}"
body_format: json
body:
name: "{{ item.name }}"
slug: "{{ item.slug }}"
color: "{{ item.color }}"
description: "{{ item.description }}"
status_code: 201
loop: "{{ tags }}"
when: item.slug not in existing_tag_slugs
register: tag_creation
changed_when: tag_creation.status == 201
# ── Custom Fields ─────────────────────────────────────────────────────
- name: Fetch existing custom fields
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-fields/?limit=200"
method: GET
headers: "{{ headers }}"
return_content: true
status_code: 200
register: existing_cf_response
- name: Set existing custom field names fact
ansible.builtin.set_fact:
existing_cf_names: "{{ existing_cf_response.json.results | map(attribute='name') | list }}"
- name: Create custom field — hostnames
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-fields/"
method: POST
headers: "{{ headers }}"
body_format: json
body:
name: hostnames
label: Hostnames
type: longtext
object_types: "{{ custom_fields[0].object_types }}"
description: "{{ custom_fields[0].description }}"
ui_visible: always
ui_editable: yes
status_code: 201
when: "'hostnames' not in existing_cf_names"
register: cf_hostnames
changed_when: cf_hostnames.status == 201
- name: Create custom field — namespace
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-fields/"
method: POST
headers: "{{ headers }}"
body_format: json
body:
name: namespace
label: Namespace
type: text
object_types: "{{ custom_fields[1].object_types }}"
description: "{{ custom_fields[1].description }}"
ui_visible: always
ui_editable: yes
status_code: 201
when: "'namespace' not in existing_cf_names"
register: cf_namespace
changed_when: cf_namespace.status == 201
- name: Create choice set for managed_by field
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-field-choice-sets/"
method: POST
headers: "{{ headers }}"
body_format: json
body:
name: managed-by-choices
extra_choices:
- - ArgoCD
- ArgoCD
- - Ansible
- Ansible
- - Manual
- Manual
status_code: [201, 400]
register: choice_set
changed_when: choice_set.status == 201
- name: Fetch choice set ID
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-field-choice-sets/?name=managed-by-choices"
method: GET
headers: "{{ headers }}"
return_content: true
status_code: 200
register: choice_set_response
- name: Create custom field — managed_by
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-fields/"
method: POST
headers: "{{ headers }}"
body_format: json
body:
name: managed_by
label: Managed By
type: select
object_types: "{{ custom_fields[2].object_types }}"
description: "{{ custom_fields[2].description }}"
choice_set: "{{ choice_set_response.json.results[0].id }}"
ui_visible: always
ui_editable: yes
status_code: 201
when: "'managed_by' not in existing_cf_names"
register: cf_managed_by
changed_when: cf_managed_by.status == 201
- name: Create custom field — thematic_name
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-fields/"
method: POST
headers: "{{ headers }}"
body_format: json
body:
name: thematic_name
label: Thematic Name
type: text
object_types: "{{ custom_fields[3].object_types }}"
description: "{{ custom_fields[3].description }}"
ui_visible: always
ui_editable: yes
status_code: 201
when: "'thematic_name' not in existing_cf_names"
register: cf_thematic_name
changed_when: cf_thematic_name.status == 201
# ── Provisioning Pipeline Fields ──────────────────────────────────────
# These fields drive the NetBox → n8n → Terraform pipeline.
# proxmox_datastore already exists — PATCH it to add utilidor choice.
# data_disk_enabled and data_disk_size_gb are new POSTs.
- name: Fetch proxmox_datastore field ID
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-fields/?name=proxmox_datastore"
method: GET
headers: "{{ headers }}"
return_content: true
status_code: 200
register: proxmox_datastore_cf_response
- name: Fetch proxmox-datastore-choices choice set ID
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-field-choice-sets/?name=proxmox-datastore-choices"
method: GET
headers: "{{ headers }}"
return_content: true
status_code: 200
register: datastore_choice_set_response
- name: Create proxmox-datastore-choices choice set if missing
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-field-choice-sets/"
method: POST
headers: "{{ headers }}"
body_format: json
body:
name: proxmox-datastore-choices
extra_choices:
- - liberty-tree
- liberty-tree
- - utilidor
- utilidor
status_code: [201, 400]
register: datastore_choice_set_create
changed_when: datastore_choice_set_create.status == 201
when: datastore_choice_set_response.json.count == 0
- name: Re-fetch proxmox-datastore-choices choice set ID after possible creation
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-field-choice-sets/?name=proxmox-datastore-choices"
method: GET
headers: "{{ headers }}"
return_content: true
status_code: 200
register: datastore_choice_set_response
- name: Patch proxmox_datastore field to use choice set with utilidor
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-fields/{{ proxmox_datastore_cf_response.json.results[0].id }}/"
method: PATCH
headers: "{{ headers }}"
body_format: json
body:
choice_set: "{{ datastore_choice_set_response.json.results[0].id }}"
status_code: 200
when: proxmox_datastore_cf_response.json.count > 0
register: cf_datastore_patch
changed_when: cf_datastore_patch.status == 200
- name: Create custom field — data_disk_enabled
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-fields/"
method: POST
headers: "{{ headers }}"
body_format: json
body:
name: data_disk_enabled
label: Data Disk Enabled
type: boolean
object_types:
- virtualization.virtualmachine
description: "When true, Terraform provisions a second disk for application data storage."
default: false
ui_visible: always
ui_editable: yes
status_code: 201
when: "'data_disk_enabled' not in existing_cf_names"
register: cf_data_disk_enabled
changed_when: cf_data_disk_enabled.status == 201
- name: Create custom field — data_disk_size_gb
ansible.builtin.uri:
url: "{{ netbox_api }}/extras/custom-fields/"
method: POST
headers: "{{ headers }}"
body_format: json
body:
name: data_disk_size_gb
label: Data Disk Size (GB)
type: integer
object_types:
- virtualization.virtualmachine
description: "Size in GB for the optional second data disk. Only used when data_disk_enabled is true."
ui_visible: always
ui_editable: yes
status_code: 201
when: "'data_disk_size_gb' not in existing_cf_names"
register: cf_data_disk_size
changed_when: cf_data_disk_size.status == 201
# ── Summary ───────────────────────────────────────────────────────────
- name: Summary
ansible.builtin.debug:
msg:
- "Tags created: {{ tag_creation.results | selectattr('status', 'equalto', 201) | list | length }}"
- "Tags skipped (already exist): {{ tag_creation.results | selectattr('skipped', 'defined') | list | length }}"
- "Custom fields configured: hostnames, namespace, managed_by, thematic_name, proxmox_datastore (patched), data_disk_enabled, data_disk_size_gb"

View File

@@ -0,0 +1,18 @@
---
# ------------------------------------------------------------------------------
# FILE: playbooks/deploy_authentik.yml
# DESCRIPTION: Deploys Authentik identity provider on guest-relations.
# Installs Docker and configures Authentik with PostgreSQL and Redis.
#
# USAGE:
# ansible-playbook -i inventory.yml playbooks/deploy_authentik.yml
# ------------------------------------------------------------------------------
- name: Deploy Authentik identity provider
hosts: authentik_server
become: true
roles:
- common
- docker-host
- authentik

View File

@@ -0,0 +1,23 @@
---
# ------------------------------------------------------------------------------
# FILE: ansible/playbooks/day1_deploy_gitea.yml
# DESCRIPTION: Deploys Gitea + PostgreSQL on mad-tea-party (10.1.71.129)
# Role chain: common → docker-host → gitea
#
# USAGE:
# ansible-playbook -i ansible/inventory.yml ansible/playbooks/day1_deploy_gitea.yml
#
# SECRETS REQUIRED IN VAULT (group_vars/all/vault):
# vault_gitea_db_password
# ------------------------------------------------------------------------------
- name: Deploy Gitea on mad-tea-party
hosts: gitea_servers
become: true
vars:
gitea_db_password: "{{ vault_gitea_db_password }}"
roles:
- role: docker-host
- role: gitea

View File

@@ -0,0 +1,79 @@
---
# =============================================================================
# day1_deploy_hermes.yml
# Deploy Hermes Agent (Nous Research) on carousel-of-progress (10.1.71.131)
#
# FIRST-RUN WORKFLOW:
# 1. Run this playbook:
# ansible-playbook playbooks/day1_deploy_hermes.yml
#
# 2. SSH to the host and run the setup wizard as the hermes user:
# ssh wed@carousel-of-progress.local.mk-labs.cloud
# sudo -u hermes hermes setup
#
# 3. Once configured, start and verify the service:
# sudo systemctl start hermes
# sudo systemctl status hermes
# sudo journalctl -u hermes -f
#
# VARIABLES:
# hermes_skip_browser: true — set to skip Playwright/Chromium install
# (saves ~300MB if browser automation not needed)
# =============================================================================
- name: Deploy Hermes Agent on carousel-of-progress
hosts: carousel-of-progress
gather_facts: true
pre_tasks:
- name: Verify target is carousel-of-progress
ansible.builtin.assert:
that:
- inventory_hostname == "carousel-of-progress"
fail_msg: >
This playbook is scoped to carousel-of-progress only.
Got: {{ inventory_hostname }}
- name: Confirm OS is Ubuntu
ansible.builtin.assert:
that:
- ansible_distribution == "Ubuntu"
fail_msg: >
This playbook requires Ubuntu. Found: {{ ansible_distribution }}.
(If running Fedora, swap apt tasks for dnf and adjust Playwright deps.)
roles:
- role: hermes
vars:
hermes_skip_browser: false # set true to skip Chromium install
post_tasks:
- name: Verify hermes binary is accessible system-wide
ansible.builtin.command: hermes --version
register: hermes_version_check
changed_when: false
failed_when: hermes_version_check.rc != 0
- name: Print hermes version
ansible.builtin.debug:
msg: "{{ hermes_version_check.stdout }}"
- name: Print post-install instructions
ansible.builtin.debug:
msg:
- "============================================================"
- "Hermes installed on carousel-of-progress (10.1.71.131)"
- "============================================================"
- "Next steps:"
- " 1. SSH to the host:"
- " ssh wed@carousel-of-progress.local.mk-labs.cloud"
- " 2. Run the setup wizard as the hermes user:"
- " sudo -u hermes hermes setup"
- " 3. After config, start the service:"
- " sudo systemctl start hermes"
- " 4. Verify:"
- " sudo systemctl status hermes"
- " sudo journalctl -u hermes -f"
- "============================================================"
- "Service is ENABLED but NOT STARTED — config required first."
- "============================================================"

View File

@@ -0,0 +1,18 @@
---
# ============================================================================
# day1_deploy_honcho.yml
# ----------------------------------------------------------------------------
# Deploys Honcho + pgvector PostgreSQL on the `lincoln` host. Assumes day0
# host provisioning (linux-baseline + expand_root_lv) is already complete.
#
# Run via:
# ansible-playbook -i inventory.yml playbooks/day0_provision.yml -e target=lincoln
# ansible-playbook -i inventory.yml playbooks/day1_deploy_honcho.yml
# ============================================================================
- name: Deploy Honcho on lincoln
hosts: honcho_server
become: true
gather_facts: true
roles:
- honcho

View File

@@ -0,0 +1,24 @@
---
# ============================================================================
# day1_deploy_jmri.yml
# ----------------------------------------------------------------------------
# Deploys JMRI JmriFaceless headless server on main-street-station.
# Applies linux-baseline first, then the jmri role.
#
# Usage:
# ansible-playbook playbooks/day1_deploy_jmri.yml
# ansible-playbook playbooks/day1_deploy_jmri.yml -e target=main-street-station
#
# Prerequisites:
# 1. Host is in inventory under jmri_server group
# 2. jmri_profile_id is set in host_vars/main-street-station.yml
# 3. SSH access as 'wed' with sudo
# ============================================================================
- name: Deploy JMRI headless server
hosts: "{{ target | default('jmri_server') }}"
become: true
gather_facts: true
roles:
- linux-baseline
- jmri

View File

@@ -0,0 +1,91 @@
---
# ------------------------------------------------------------------------------
# FILE: playbooks/day1_deploy_nextcloud.yml
# DESCRIPTION: Deploys Nextcloud on the-grid
# Runs: docker-host → nextcloud
#
# USAGE:
# ansible-playbook -i inventory.yml playbooks/day1_deploy_nextcloud.yml
# ansible-playbook -i inventory.yml playbooks/day1_deploy_nextcloud.yml --limit the-grid
#
# SECRETS REQUIRED IN VAULT (group_vars/all/vault):
# vault_nextcloud_db_root_password
# vault_nextcloud_db_password
# vault_nextcloud_admin_user
# vault_nextcloud_admin_password
# ------------------------------------------------------------------------------
- name: Deploy Nextcloud on the-grid
hosts: the-grid
become: true
vars:
# NFS prerequisite
nfs_packages:
- nfs-common
# Compose stack location
nextcloud_base_dir: /opt/docker/nextcloud
# Secrets from vault
nextcloud_db_root_password: "{{ vault_nextcloud_db_root_password }}"
nextcloud_db_password: "{{ vault_nextcloud_db_password }}"
nextcloud_admin_user: "{{ vault_nextcloud_admin_user }}"
nextcloud_admin_password: "{{ vault_nextcloud_admin_password }}"
pre_tasks:
- name: Install NFS client
ansible.builtin.apt:
name: "{{ nfs_packages }}"
state: present
update_cache: true
roles:
- role: docker-host
tasks:
- name: Create Nextcloud directory
ansible.builtin.file:
path: "{{ nextcloud_base_dir }}"
state: directory
owner: "{{ ansible_user }}"
group: docker
mode: "0775"
- name: Deploy Compose file
ansible.builtin.copy:
src: "{{ playbook_dir }}/../../boilerplates/nextcloud/compose.yml"
dest: "{{ nextcloud_base_dir }}/compose.yml"
owner: "{{ ansible_user }}"
group: docker
mode: "0644"
- name: Deploy .env from vault
ansible.builtin.copy:
content: |
# Managed by Ansible — do not edit manually
MYSQL_ROOT_PASSWORD={{ nextcloud_db_root_password }}
MYSQL_PASSWORD={{ nextcloud_db_password }}
NEXTCLOUD_ADMIN_USER={{ nextcloud_admin_user }}
NEXTCLOUD_ADMIN_PASSWORD={{ nextcloud_admin_password }}
dest: "{{ nextcloud_base_dir }}/.env"
owner: "{{ ansible_user }}"
group: docker
mode: "0600"
- name: Start Nextcloud stack
community.docker.docker_compose_v2:
project_src: "{{ nextcloud_base_dir }}"
state: present
- name: Wait for Nextcloud to become ready
ansible.builtin.uri:
url: "http://the-grid.local.mk-labs.cloud/status.php"
status_code: 200
return_content: true
register: nextcloud_status
until: >
nextcloud_status.status == 200 and
(nextcloud_status.content | from_json).installed == true
retries: 20
delay: 15

View File

@@ -0,0 +1,35 @@
---
# ------------------------------------------------------------------------------
# FILE: ansible/playbooks/day1_deploy_ollama.yml
# DESCRIPTION: Deploys Ollama with ROCm GPU acceleration on astro-orbiter.
# Assumes day0_baseline.yml has already run (common role complete).
# PCIe passthrough for the RX 5700 must be configured in Proxmox
# and the GPU must be visible to the VM before running this playbook.
#
# Pre-flight check:
# ssh wed@astro-orbiter 'lspci | grep -i amd'
# Should show the RX 5700 before proceeding.
#
# Usage (from ansible/ directory):
# ansible-playbook playbooks/day1_deploy_ollama.yml
# ------------------------------------------------------------------------------
- name: Deploy Ollama with ROCm on astro-orbiter
hosts: astro-orbiter
become: true
pre_tasks:
- name: Verify AMD GPU is visible to the VM
command: lspci
register: lspci_output
changed_when: false
- name: Fail if no AMD GPU detected
fail:
msg: >
No AMD GPU detected via lspci. Verify PCIe passthrough is configured
in Proxmox and the RX 5700 is visible to the VM before proceeding.
when: "'AMD' not in lspci_output.stdout and 'Radeon' not in lspci_output.stdout"
roles:
- ollama

View File

@@ -0,0 +1,17 @@
---
# ============================================================================
# day1_deploy_semaphore.yml
# ----------------------------------------------------------------------------
# Deploys SemaphoreUI + PostgreSQL on the imagineering host (figment).
# Run AFTER day0_linux_baseline.yml has been applied to the target.
#
# Usage:
# ansible-playbook -i inventory.yml playbooks/day1_deploy_semaphore.yml
# ============================================================================
- name: Deploy SemaphoreUI on imagineering
hosts: semaphore_server
become: true
gather_facts: true
roles:
- semaphore

View File

@@ -0,0 +1,18 @@
---
# ------------------------------------------------------------------------------
# FILE: playbooks/deploy_traefik.yml
# DESCRIPTION: Deploys Traefik reverse proxy on lightning-lane.
# Installs Docker and configures Traefik with Cloudflare DNS-01
# certificate resolution for *.local.mk-labs.cloud
#
# USAGE:
# ansible-playbook -i inventory.yml playbooks/deploy_traefik.yml
# ------------------------------------------------------------------------------
- name: Deploy Traefik reverse proxy
hosts: load_balancers
become: true
roles:
- docker-host
- traefik

View File

@@ -0,0 +1,7 @@
---
- name: Apply common role
hosts: "{{ target | default('all') }}"
become: true
roles:
- common
- docker-host

View File

@@ -0,0 +1,80 @@
---
# ------------------------------------------------------------------------------
# FILE: ansible/playbooks/day2_install_node_exporter.yml
# DESCRIPTION: Install and configure Prometheus Node Exporter on Linux hosts.
#
# Targets two groups with different firewall managers:
# - proxmox_nodes : Proxmox hypervisors (Debian, firewalld not present,
# uses iptables/no firewall — just open the port via UFW
# if present, otherwise skip)
# - monitored_vms : Ubuntu VMs managed by UFW
#
# After installing on new hosts, re-runs day1_deploy_monitoring.yml to refresh
# the Prometheus scrape config is NOT needed — targets are already statically
# defined in prometheus.yaml.j2 for the Proxmox nodes. For new VMs, add the
# IP to the prometheus.yaml.j2 proxmox-vms job and re-run day1_deploy_monitoring.yml.
#
# Usage:
# ansible-playbook -i inventory.yml playbooks/day2_install_node_exporter.yml
# ansible-playbook -i inventory.yml playbooks/day2_install_node_exporter.yml --limit proxmox_nodes
# ansible-playbook -i inventory.yml playbooks/day2_install_node_exporter.yml --limit monitored_vms
# ------------------------------------------------------------------------------
# ── Play 1: Proxmox hypervisors ───────────────────────────────────────────────
# Proxmox runs Debian. The prometheus.prometheus.node_exporter role installs
# a binary + systemd service without touching apt, which is what we want on
# hypervisors (keep the package footprint clean).
# Note: Proxmox does not run UFW. Port 9100 is open by default on VLAN 71.
- name: Install Node Exporter on Proxmox hypervisors
hosts: proxmox_nodes
become: true
vars:
node_exporter_version: "1.9.1"
node_exporter_web_listen_address: "0.0.0.0:9100"
node_exporter_enabled_collectors:
- systemd
- processes
- filesystem
- meminfo
- cpu
- diskstats
- netdev
- loadavg
- uname
roles:
- role: prometheus.prometheus.node_exporter
# ── Play 2: Ubuntu VMs ────────────────────────────────────────────────────────
# Standard Ubuntu hosts with UFW. Same role, adds UFW allow rule for 9100.
- name: Install Node Exporter on monitored VMs
hosts: monitored_vms
become: true
vars:
node_exporter_version: "1.9.1"
node_exporter_web_listen_address: "0.0.0.0:9100"
node_exporter_enabled_collectors:
- systemd
- processes
- filesystem
- meminfo
- cpu
- diskstats
- netdev
- loadavg
- uname
pre_tasks:
- name: Open Node Exporter port in UFW
community.general.ufw:
rule: allow
port: "9100"
proto: tcp
comment: "Prometheus Node Exporter"
roles:
- role: prometheus.prometheus.node_exporter

View File

@@ -0,0 +1,5 @@
- name: 1. Deploy n8n server
hosts: n8n_server
gather_facts: true
roles:
- role: n8n

View File

@@ -0,0 +1,5 @@
- name: 1. Deploy NTP servers
hosts: ntp_servers
gather_facts: true
roles:
- role: ntp-server

View File

@@ -0,0 +1,29 @@
---
# ------------------------------------------------------------------------------
# FILE: playbooks/deploy_step_ca.yml
# DESCRIPTION: Deploys Smallstep step-ca SSH Certificate Authority on turnstile.
# Installs Docker and configures step-ca with SSH certificate support.
#
# PREREQUISITES:
# - VM provisioned via Terraform
# - DNS record for turnstile.local.mk-labs.cloud on monorail
# - Authentik OIDC application created (for post-init provisioner setup)
#
# USAGE:
# ansible-playbook -i inventory.yml playbooks/deploy_step_ca.yml
#
# POST-DEPLOY:
# 1. Note the CA fingerprint from the init output
# 2. Add the OIDC provisioner (see docs/guides/step-ca-setup.md)
# 3. Deploy Traefik route via update_traefik_routes.yml
# 4. Bootstrap client workstations with: step ca bootstrap
# ------------------------------------------------------------------------------
- name: Deploy step-ca SSH Certificate Authority
hosts: step_ca_server
become: true
roles:
- common
- docker-host
- step-ca

View File

@@ -0,0 +1,7 @@
---
- name: Enroll host as step-ca SSH client
hosts: "{{ target | default('all') }}"
become: true
tasks:
- name: Include step-ca client enrollment
ansible.builtin.include_tasks: roles/common/tasks/step_ca_client.yml

View File

@@ -0,0 +1,67 @@
---
- name: Master playbook to install and configure prometheus
hosts: prometheus_server
become: true
tasks:
- name: Install prerequisite software
ansible.builtin.dnf:
name:
- tar
- python3-dnf
state: present
- name: Permit prometheus metrics in default zone for dns service
ansible.posix.firewalld:
port: 9100/tcp
permanent: true
immediate: true
state: enabled
- name: Permit traffic in default zone for dns service
ansible.posix.firewalld:
port: 9090/tcp
permanent: true
immediate: true
state: enabled
- name: Install prometheus via role
ansible.builtin.import_role:
name: prometheus.prometheus.prometheus
vars:
prometheus_targets:
node:
- targets:
- localhost:9100
labels:
env: mk-labs
- name: Permit grafana in default zone for dns service
ansible.posix.firewalld:
port: 3000/tcp
permanent: true
immediate: true
state: enabled
- name: Install grafana via role
ansible.builtin.import_role:
name: grafana.grafana.grafana
# - name: Create/Update Data sources
# grafana.grafana.datasource:
# dataSource: |
# {
# "name": "Prometheus",
# "type": "prometheus",
# "access": "proxy",
# "url": "http://localhost:9090",
# "jsonData": {
# "httpMethod": "POST",
# "manageAlerts": true,
# "prometheusType": "Prometheus",
# "cacheLevel": "High"
# }
# }
# grafana_url: "{{ grafana_url }}"
# grafana_api_key: "{{ grafana_api_key }}"
# state: present

View File

@@ -0,0 +1,15 @@
---
# ------------------------------------------------------------------------------
# FILE: playbooks/install_talosctl.yml
# DESCRIPTION: Install talosctl on city-hall for managing the fastpass cluster.
# USAGE:
# cd ansible
# ansible-playbook -i inventory.yml playbooks/install_talosctl.yml
# ------------------------------------------------------------------------------
- name: Install talosctl
hosts: talos_control
become: true
roles:
- talosctl

View File

@@ -0,0 +1,16 @@
---
- name: Master playbook to install and configure prometheus node explorer
hosts: prometheus_server
become: true
tasks:
- name: Permit prometheus metrics in default zone for dns service
ansible.posix.firewalld:
port: 9100/tcp
permanent: true
immediate: true
state: enabled
- name: Install prometheus node exporter via role
ansible.builtin.import_role:
name: prometheus.prometheus.node_exporter

View File

@@ -0,0 +1,31 @@
---
- name: Create VM ID from IP address for Proxmox hosts
hosts: all
gather_facts: false
tasks:
- name: Modify Proxmox Ubuntu VM
delegate_to: "localhost"
when: platform is defined and platform == "proxmox"
block:
- name: Update cloud-init file
community.proxmox.proxmox_kvm:
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password }}"
api_host: "{{ proxmox_host }}"
node: "{{ proxmox_clone_node }}"
vmid: "{{ vm_id }}"
ipconfig:
ipconfig0: "ip=dhcp"
update: true
# - name: Add VM to HA group
# community.proxmox.proxmox_cluster_ha_resources:
# api_user: "{{ proxmox_user }}"
# api_password: "{{ proxmox_password }}"
# api_host: "{{ proxmox_host }}"
# name: vm:"{{ vm_id }}"
# state: "present"
# group: "{{ ha_group }}"
# max_relocate: 2
# max_restart: 2

View File

@@ -0,0 +1,143 @@
---
- name: Install Prometheus and Grafana on control node
hosts: prometheus_server
become: true
# vars_files:
# - vars.yml
tasks:
- name: Install prerequisite software
ansible.builtin.dnf:
name:
- tar
- wget
state: present
- name: Download Prometheus
ansible.builtin.get_url:
url: "{{ prometheus_installer_download_url }}"
dest: "/tmp/{{ prometheus_installer_file }}"
- name: Extract Prometheus
ansible.builtin.unarchive:
src: "/tmp/{{ prometheus_installer_file }}"
dest: "/usr/local/bin/"
remote_src: true
- name: Create Prometheus user
user:
name: prometheus
shell: /bin/false
state: present
- name: Create Prometheus directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: prometheus
group: prometheus
with_items:
- /etc/prometheus
- /var/lib/prometheus
- name: Move Prometheus binaries
ansible.builtin.command: "mv /usr/local/bin/prometheus-{{ prometheus_version }}.linux-amd64/prometheus /usr/local/bin/prometheus"
- name: Move Prometheus tool
ansible.builtin.command: "mv /usr/local/bin/prometheus-{{ prometheus_version }}.linux-amd64/promtool /usr/local/bin/promtool"
- name: Create Prometheus configuration file
ansible.builtin.copy:
dest: "/etc/prometheus/prometheus.yml"
content: |
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'nodes'
static_configs:
- targets:
{% for ip in prometheus_nodes %}
- '{{ ip }}:9100'
{% endfor %}
- name: Create Prometheus service file
ansible.builtin.copy:
dest: "/etc/systemd/system/prometheus.service"
content: |
[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/var/lib/prometheus/ \
--web.console.templates=/etc/prometheus/consoles \
--web.console.libraries=/etc/prometheus/console_libraries
[Install]
WantedBy=multi-user.target
register: prometheus_service_status
ignore_errors: true
- name: Check if Prometheus service is running
ansible.builtin.command: systemctl is-active prometheus
register: prometheus_status
changed_when: false
ignore_errors: true
- name: Permit prometheus endpoint in default zone for dns service
firewalld:
port: 9090/tcp
permanent: true
immediate: true
state: enabled
- name: Permit prometheus metrics in default zone for dns service
firewalld:
port: 9100/tcp
permanent: true
immediate: true
state: enabled
- name: Permit grafana traffic in default zone for dns service
firewalld:
port: 3000/tcp
permanent: true
immediate: true
state: enabled
- name: Manage Prometheus service state
systemd:
name: prometheus
state: "{{ 'restarted' if prometheus_status.rc == 0 else 'started' }}"
enabled: true
- name: Add repository
ansible.builtin.yum_repository:
name: grafana
description: Grafana OSS repo
baseurl: https://rpm.grafana.com
gpgkey: https://rpm.grafana.com/gpg.key
- name: Install Grafana
ansible.builtin.dnf:
name:
- grafana
state: present
- name: Start Grafana service
systemd:
name: grafana-server
state: started
enabled: true

View File

@@ -22,7 +22,8 @@
name: "{{ inventory_hostname }}" name: "{{ inventory_hostname }}"
node: "{{ proxmox_clone_node }}" node: "{{ proxmox_clone_node }}"
storage: "{{ vm_storage }}" storage: "{{ vm_storage }}"
format: raw format: qcow2 # raw
timeout: 600
# - name: Add VM to HA group # - name: Add VM to HA group
# community.proxmox.proxmox_cluster_ha_resources: # community.proxmox.proxmox_cluster_ha_resources:

View File

@@ -0,0 +1,50 @@
---
- name: Create VM ID from IP address for Proxmox hosts
hosts: all
gather_facts: false
tasks:
- name: Create Proxmox VMs
delegate_to: "localhost"
when: platform is defined and platform == "proxmox"
block:
- name: Create VM ID from IP address
ansible.builtin.set_fact:
vm_id: "{{ ip_address.split('.')[-2] }}{{ '%03d' % (ip_address.split('.')[-1] | int) }}"
- name: Create VM with an EFI disk, with Secure Boot disabled by default
community.proxmox.proxmox_kvm:
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password }}"
api_host: "{{ proxmox_host }}"
name: "{{ inventory_hostname }}"
node: "{{ proxmox_clone_node }}"
vmid: "{{ vm_id }}"
net:
net0: "virtio,bridge=vmbr0"
ide:
ide0: "{{ vm_iso_storage }}:iso/{{ vm_iso }},media=cdrom"
scsihw: "virtio-scsi-single"
scsi:
scsi0: "{{ vm_storage }}:{{ vm_definitions[vm_size]['disk'] }},format=qcow2"
bios: "ovmf"
cpu: "x86-64-v2"
cores: "{{ vm_definitions[vm_size]['cores'] }}"
memory: "{{ vm_definitions[vm_size]['memory'] }}"
efidisk0:
storage: "{{ vm_storage }}"
format: raw
efitype: 4m
pre_enrolled_keys: false
timeout: 600
# - name: Add VM to HA group
# community.proxmox.proxmox_cluster_ha_resources:
# api_user: "{{ proxmox_user }}"
# api_password: "{{ proxmox_password }}"
# api_host: "{{ proxmox_host }}"
# name: vm:"{{ vm_id }}"
# state: "present"
# group: "{{ ha_group }}"
# max_relocate: 2
# max_restart: 2

View File

@@ -0,0 +1,40 @@
---
- name: Create VM ID from IP address for Proxmox hosts
hosts: all
gather_facts: false
tasks:
- name: Start Proxmox VM
delegate_to: "localhost"
when: platform is defined and platform == "proxmox"
community.proxmox.proxmox_kvm:
api_user: "{{ proxmox_user }}"
api_password: "{{ proxmox_password }}"
api_host: "{{ proxmox_host }}"
name: "{{ inventory_hostname }}"
node: "{{ proxmox_clone_node }}"
state: started
- name: Wait for SSH service to be ready
delegate_to: "localhost"
when: platform is defined and platform == "proxmox"
ansible.builtin.wait_for:
port: 22
host: "{{ inventory_hostname }}"
search_regex: OpenSSH
timeout: 300
msg: "Waiting for SSH service to be ready on {{ inventory_hostname }}"
- name: Wait for VM to be fully booted and responsive
delegate_to: "localhost"
when: platform is defined and platform == "proxmox"
ansible.builtin.wait_for_connection:
connect_timeout: 10
sleep: 5
timeout: 300
- name: Display VM startup completion
delegate_to: "localhost"
when: platform is defined and platform == "proxmox"
ansible.builtin.debug:
msg: "✅ {{ inventory_hostname }} is now running and accessible via SSH"

View File

@@ -9,4 +9,8 @@
delegate_to: "{{ inventory_hostname }}" delegate_to: "{{ inventory_hostname }}"
ansible.builtin.hostname: ansible.builtin.hostname:
name: "{{ hostname }}" name: "{{ hostname }}"
#delay: 20
- name: Set timezone to US/Central
become: true
community.general.timezone:
name: US/Central

View File

@@ -0,0 +1,42 @@
apiVersion: v1alpha1
kind: AgentConfig
metadata:
name: {{ hostvars[groups[cluster_group][0]].cluster_name }}
rendezvousIP: {{ hostvars[groups[cluster_group][0]].ip_address }}
bootArtifactsBaseURL: {{ hostvars[groups['matchbox_server'][0]].http_endpoint }}/assets/hub/
hosts:
{% for host in groups[cluster_group] %}
- hostname: {{ host }}
role: {{ hostvars[host].node_role | default('master') }}
interfaces:
- name: {{ hostvars[host].primary_interface }}
macAddress: {{ hostvars[host].install_mac_address }}
networkConfig:
interfaces:
- name: {{ hostvars[host].primary_interface }}
type: ethernet
state: up
identifier: mac-address
mac-address: {{ hostvars[host].install_mac_address }}
ipv4:
enabled: true
dhcp: true
{% if hostvars[host].boot_mac_address != hostvars[host].install_mac_address %}
- name: {{ hostvars[host].boot_interface }}
type: ethernet
state: down
identifier: mac-address
mac-address: {{ hostvars[host].boot_mac_address }}
{% endif %}
dns-resolver:
config:
server:
- 10.1.71.251
- 10.1.71.252
routes:
config:
- destination: 0.0.0.0/0
next-hop-address: 10.1.71.1
next-hop-interface: {{ hostvars[host].primary_interface }}
table-id: 254
{% endfor %}

View File

@@ -0,0 +1,14 @@
// Matcher group for OCP Internal machines
{% for host in groups[cluster_group] %}
resource "matchbox_group" "{{ host }}" {
name = "{{ host }}" # Physical Server
profile = matchbox_profile.openshift-agent-install.name
selector = {
{% if hostvars[host].boot_mac_address == hostvars[host].install_mac_address %}
mac = "{{ hostvars[host].boot_mac_address }}" # PXE boots and installs to same NIC
{% else %}
mac = "{{ hostvars[host].boot_mac_address }}" # PXE boots to 1GbE NIC, installs to 10GbE NIC
{% endif %}
}
}
{% endfor %}

View File

@@ -0,0 +1,33 @@
apiVersion: v1
baseDomain: {{ hostvars[groups[cluster_group][0]].base_domain }}
compute:
- name: worker
hyperthreading: Enabled
replicas: {{ hostvars[groups[cluster_group][0]].worker_node_count }}
controlPlane:
hyperthreading: Enabled
name: master
replicas: {{ hostvars[groups[cluster_group][0]].master_node_count }}
metadata:
name: {{ hostvars[groups[cluster_group][0]].cluster_name }}
networking:
clusterNetwork:
- cidr: {{ hostvars[groups[cluster_group][0]].cluster_network }}
hostPrefix: {{ hostvars[groups[cluster_group][0]].cluster_network_host_prefix }}
machineNetwork:
- cidr: {{ hostvars[groups[cluster_group][0]].machine_network }}
networkType: OVNKubernetes
serviceNetwork:
- {{ hostvars[groups[cluster_group][0]].service_network }}
platform:
{% if hostvars[groups[cluster_group][0]].platform_type == 'none' %}
{{ hostvars[groups['hub_cluster'][0]].platform_type }}: {}
{% endif %}
{% if hostvars[groups[cluster_group][0]].platform_type == 'baremetal' %}
{{ hostvars[groups[cluster_group][0]].platform_type }}:
apiVIP: {{ hostvars[groups[cluster_group][0]].api_address }}
ingressVIP: {{ hostvars[groups[cluster_group][0]].app_address }}
{% endif %}
fips: false
pullSecret: '{{ vault_pull_secret }}'
sshKey: '{{ vault_ssh_key }}'

View File

@@ -0,0 +1,28 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: kubeadm-config
namespace: kube-system
data:
ClusterConfiguration: |
apiServer: {}
apiVersion: kubeadm.k8s.io/v1beta4
caCertificateValidityPeriod: 87600h0m0s
certificateValidityPeriod: 8760h0m0s
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns: {}
encryptionAlgorithm: RSA-2048
etcd:
local:
dataDir: /var/lib/etcd
imageRepository: registry.k8s.io
kind: ClusterConfiguration
kubernetesVersion: {{ kubernetes_version | default('v1.33.4') }}
networking:
dnsDomain: cluster.local
serviceSubnet: {{ service_cidr | default('10.96.0.0/12') }}
proxy: {}
scheduler: {}
controlPlaneEndpoint: {{ control_plane_endpoint | default('fastpass.local.mk-labs.cloud') }}:{{ control_plane_port | default('6443') }}

View File

@@ -0,0 +1,16 @@
// Fedora CoreOS profile
resource "matchbox_profile" "openshift-agent-install" {
name = "{{ hostvars[groups[cluster_group][0]].cluster_name }}"
kernel = "/assets/{{ hostvars[groups[cluster_group][0]].cluster_name }}/agent.x86_64-vmlinuz"
initrd = [
"--name initrd /assets/{{ hostvars[groups[cluster_group][0]].cluster_name }}/agent.x86_64-initrd.img"
]
args = [
"initrd=initrd",
"coreos.live.rootfs_url={{ hostvars[groups['matchbox_server'][0]].http_endpoint }}/assets/{{ hostvars[groups[cluster_group][0]].cluster_name }}/agent.x86_64-rootfs.img",
"rw",
"ignition.firstboot",
"ignition.platform.id=metal",
]
}

View File

@@ -1,6 +1,6 @@
// Configure the matchbox provider // Configure the matchbox provider
provider "matchbox" { provider "matchbox" {
endpoint = "{{ hostvars[groups['matchbox'][0]].rpc_endpoint }}" endpoint = "{{ hostvars[groups['matchbox_server'][0]].rpc_endpoint }}"
client_cert = file("/etc/matchbox/client.crt") client_cert = file("/etc/matchbox/client.crt")
client_key = file("/etc/matchbox/client.key") client_key = file("/etc/matchbox/client.key")
ca = file("/etc/matchbox/ca.crt") ca = file("/etc/matchbox/ca.crt")

View File

@@ -0,0 +1,8 @@
resource "unifi_user" "{{ inventory_hostname }}" {
mac = "{{ vm_mac_address }}"
name = "{{ inventory_hostname }}"
# note = "my note"
fixed_ip = "{{ ip_address }}"
allow_existing = true
}

View File

@@ -0,0 +1,22 @@
// Configure the unifi provider
terraform {
required_providers {
unifi = {
source = "paultyng/unifi"
version = "0.41.0"
}
}
}
provider "unifi" {
username = "terraform" # optionally use UNIFI_USERNAME env var
password = "{{ vault_unifi_password }}" # optionally use UNIFI_PASSWORD env var
api_url = "https://192.168.1.1" # optionally use UNIFI_API env var
# you may need to allow insecure TLS communications unless you have configured
# certificates for your controller
allow_insecure = "true" # optionally use UNIFI_INSECURE env var
# if you are not configuring the default site, you can change the site
# site = "world-drive" # or optionally use UNIFI_SITE env var
}

View File

@@ -0,0 +1,11 @@
---
- name: Test DNS CNAME Creation
hosts: fastpass_control_plane[0]
gather_facts: false
tasks:
- name: Test DNS CNAME entry creation
ansible.builtin.include_role:
name: dns-manager
vars:
cluster_endpoint: "{{ control_plane_endpoint }}"
cluster_cname_target: "{{ traefik_server }}.{{ base_domain }}"

25
ansible/playbooks/test.yml Executable file
View File

@@ -0,0 +1,25 @@
---
- name: Test Ansible Playbook
hosts: all
gather_facts: yes
become: false
tasks:
- name: Ping all hosts
ansible.builtin.ping:
- name: Display hostname
ansible.builtin.debug:
msg: "The hostname of this system is {{ inventory_hostname }}"
- name: Show OS distribution
ansible.builtin.debug:
msg: "This host is running {{ ansible_distribution }} {{ ansible_distribution_version }}"
- name: Run uptime command
ansible.builtin.command: uptime
register: uptime_result
- name: Display uptime result
ansible.builtin.debug:
var: uptime_result.stdout

View File

@@ -0,0 +1,16 @@
---
# FILE: playbooks/test_connectivity.yml
# Simple test playbook to validate Semaphore can connect and run tasks
- name: Test connectivity
hosts: all
gather_facts: true
tasks:
- name: Print hostname
ansible.builtin.debug:
msg: "Connected to {{ inventory_hostname }} ({{ ansible_hostname }})"
- name: Print OS info
ansible.builtin.debug:
msg: "{{ ansible_distribution }} {{ ansible_distribution_version }}"

Some files were not shown because too many files have changed in this diff Show More