Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ permissions:
pull-requests: read

jobs:
scan:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6.0.2
with:
fetch-depth: 0

- name: Install Infisical CLI
run: |
curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | sudo -E bash
sudo apt-get update && sudo apt-get install -y infisical

- name: Scan for committed secrets
run: infisical scan --config .infisical-scan.toml --verbose --redact

lint:
runs-on: ubuntu-latest
permissions:
Expand Down Expand Up @@ -63,7 +80,7 @@ jobs:
uses: helm/kind-action@v1.14.0

deploy:
needs: [lint]
needs: [scan, lint]
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ kubeconfig
*.bak
*.backup

scripts/.venv

# Terraform (if used later)
.terraform/
*.tfstate
Expand Down
25 changes: 25 additions & 0 deletions .infisical-scan.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Infisical secret-scanning config (gitleaks-compatible).
# Extends the default ruleset, then allowlists paths that hold
# intentionally-committed, non-sensitive data.
title = "homelab-k8s secret scan"

[extend]
useDefault = true

[allowlist]
description = "Paths and content safe to ignore"
regexes = [
# kubeseal SealedSecret ciphertext — encrypted, not a plaintext leak. Needed
# in addition to the path rule below because the pre-commit hook scans a
# piped diff (no file path), so path allowlists don't apply there.
'''Ag[A-Za-z0-9+/=]{60,}''',
]
paths = [
# SealedSecret resources contain kubeseal ciphertext, not plaintext creds.
'''charts/.*/sealed-secrets/.*-sealed\.yaml''',
# Secret templates hold REPLACE_WITH_* placeholders, not real values.
'''.*\.template$''',
# Lockfiles / vendored deps.
'''.*Chart\.lock$''',
'''scripts/uv\.lock$''',
]
12 changes: 12 additions & 0 deletions .infisicalignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Acknowledged scan findings — all triaged as non-sensitive or dead.
# Format: <commit>:<path>:<rule>:<line>
#
# beszel/power-monitor charts are deleted from the tree; findings are
# false positives (an SSH *public* key and a commented-out placeholder
# in a .example file). media-stack.yml was only ever on the local,
# unpushed `openclaw` branch and is now gitignored.
c6ff6e9afa20fdbe6caf93bc2a272017620c4ca2:docker/media-stack.yml:generic-api-key:13
8851fa7d3baa068141a6ead924bc687dcb5f50dd:charts/power-monitor/values.example.yaml:private-key:69
74ec70b5d7fb107ab39bcba326b6bce846af13a2:charts/power-monitor/values.example.yaml:private-key:69
eb4dc55b7e8d1fcc0cad79b837d1384a650631dd:charts/beszel/values.yaml:generic-api-key:86
4f04b2e7587edba63eefb01c5b4e55a752ccaedc:charts/beszel/values.yaml:generic-api-key:86
21 changes: 21 additions & 0 deletions .yamllint
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
# Shared yamllint config for `make lint` and the pre-commit hook.
extends: default

rules:
# Homelab values files have long descriptions/URLs; warn, don't fail.
line-length:
max: 160
level: warning
# `---` document-start is optional in this repo.
document-start: disable
# Helm/k8s use bare on/off/yes; don't treat as booleans.
truthy:
check-keys: false
comments:
min-spaces-from-content: 1

ignore: |
charts/*/templates/
charts/*/charts/
charts/*/sealed-secrets/
43 changes: 23 additions & 20 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ help:
@echo "Homelab K8s Makefile"
@echo ""
@echo "Setup Commands:"
@echo " make setup-hooks Install git pre-commit hook (auto-bumps chart versions)"
@echo " make setup-hooks Install git pre-commit hook (secret scan, lint, version bump)"
@echo " make setup-repos Add all required Helm repositories"
@echo " make build-deps Build chart dependencies from Chart.lock"
@echo " make update-deps Update dependencies and regenerate Chart.lock files"
Expand Down Expand Up @@ -51,9 +51,9 @@ help:
# Setup Commands
setup-hooks:
@echo "Installing git pre-commit hook..."
@cp scripts/pre-commit-chart-version.sh .git/hooks/pre-commit
@cp scripts/pre-commit.sh .git/hooks/pre-commit
@chmod +x .git/hooks/pre-commit
@echo "✓ Pre-commit hook installed (auto-bumps chart patch versions)"
@echo "✓ Pre-commit hook installed (secret scan, yamllint, helm lint, hygiene, version bump)"

setup-repos:
@echo "Adding Helm repositories..."
Expand All @@ -76,6 +76,7 @@ build-deps:
helm dependency build ./charts/loki
helm dependency build ./charts/alloy
helm dependency build ./charts/infisical
helm dependency build ./charts/infisical-secrets-operator
@echo "✓ Dependencies built"

update-deps:
Expand All @@ -88,25 +89,27 @@ update-deps:
helm dependency update ./charts/loki
helm dependency update ./charts/alloy
helm dependency update ./charts/infisical
helm dependency update ./charts/infisical-secrets-operator
@echo "✓ Dependencies updated and Chart.lock files regenerated"

install-infra:
@echo "Installing infrastructure components..."
helm install metallb ./charts/metallb -n metallb-system --create-namespace $(HELM_GLOBAL_FLAGS)
helm install cert-manager ./charts/cert-manager -n cert-manager --create-namespace $(HELM_GLOBAL_FLAGS)
helm install sealed-secrets ./charts/sealed-secrets -n kube-system $(HELM_GLOBAL_FLAGS)
helm install nginx-ingress ./charts/nginx-ingress -n default $(HELM_GLOBAL_FLAGS)
helm upgrade --install metallb ./charts/metallb -n metallb-system --create-namespace $(HELM_GLOBAL_FLAGS)
helm upgrade --install cert-manager ./charts/cert-manager -n cert-manager --create-namespace $(HELM_GLOBAL_FLAGS)
helm upgrade --install sealed-secrets ./charts/sealed-secrets -n kube-system $(HELM_GLOBAL_FLAGS)
helm upgrade --install nginx-ingress ./charts/nginx-ingress -n default $(HELM_GLOBAL_FLAGS)
helm upgrade --install infisical-secrets-operator ./charts/infisical-secrets-operator -n infisical-secrets-operator --create-namespace $(HELM_GLOBAL_FLAGS)
@echo "✓ Infrastructure installed"

install-monitoring:
@echo "Installing monitoring stack..."
helm install kube-prometheus-stack ./charts/kube-prometheus-stack -n monitoring --create-namespace $(HELM_GLOBAL_FLAGS)
helm upgrade --install kube-prometheus-stack ./charts/kube-prometheus-stack -n monitoring --create-namespace $(HELM_GLOBAL_FLAGS)
@echo "Waiting for Prometheus Operator to be ready..."
@kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=prometheus-operator -n monitoring --timeout=120s
helm install loki ./charts/loki -n monitoring $(HELM_GLOBAL_FLAGS)
helm upgrade --install loki ./charts/loki -n monitoring $(HELM_GLOBAL_FLAGS)
@echo "Waiting for Loki to be ready..."
@kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=loki -n monitoring --timeout=120s
helm install alloy ./charts/alloy -n monitoring $(HELM_GLOBAL_FLAGS)
helm upgrade --install alloy ./charts/alloy -n monitoring $(HELM_GLOBAL_FLAGS)
@echo "✓ Monitoring stack installed"
@echo ""
@echo "Access Grafana at: https://grafana.home"
Expand All @@ -115,20 +118,20 @@ install-monitoring:

deploy-all:
@echo "Installing application services..."
helm install valkey ./charts/valkey $(HELM_GLOBAL_FLAGS)
helm upgrade --install valkey ./charts/valkey $(HELM_GLOBAL_FLAGS)
@echo "Waiting for Valkey to be ready..."
@kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=valkey --timeout=120s
helm install unbound ./charts/unbound $(HELM_GLOBAL_FLAGS)
helm upgrade --install unbound ./charts/unbound $(HELM_GLOBAL_FLAGS)
@echo "Waiting for Unbound to be ready..."
@kubectl wait --for=condition=ready pod -l app=unbound --timeout=120s
helm install pihole ./charts/pihole $(HELM_GLOBAL_FLAGS)
helm install immich ./charts/immich $(HELM_GLOBAL_FLAGS)
helm install authentik ./charts/authentik $(HELM_GLOBAL_FLAGS)
helm install infisical ./charts/infisical $(HELM_GLOBAL_FLAGS)
helm install home-assistant ./charts/home-assistant $(HELM_GLOBAL_FLAGS)
helm install restic-backup ./charts/restic-backup $(HELM_GLOBAL_FLAGS)
helm install immich-db-backup ./charts/immich-db-backup $(HELM_GLOBAL_FLAGS)
helm install home-assistant-backup ./charts/home-assistant-backup $(HELM_GLOBAL_FLAGS)
helm upgrade --install pihole ./charts/pihole $(HELM_GLOBAL_FLAGS)
helm upgrade --install immich ./charts/immich $(HELM_GLOBAL_FLAGS)
helm upgrade --install authentik ./charts/authentik $(HELM_GLOBAL_FLAGS)
helm upgrade --install infisical ./charts/infisical $(HELM_GLOBAL_FLAGS)
helm upgrade --install home-assistant ./charts/home-assistant $(HELM_GLOBAL_FLAGS)
helm upgrade --install restic-backup ./charts/restic-backup $(HELM_GLOBAL_FLAGS)
helm upgrade --install immich-db-backup ./charts/immich-db-backup $(HELM_GLOBAL_FLAGS)
helm upgrade --install home-assistant-backup ./charts/home-assistant-backup $(HELM_GLOBAL_FLAGS)
@echo "✓ Services installed"

install-all: setup-repos build-deps install-infra install-monitoring deploy-all
Expand Down
6 changes: 6 additions & 0 deletions charts/infisical-secrets-operator/Chart.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
dependencies:
- name: secrets-operator
repository: https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/
version: 0.10.33
digest: sha256:0acac565b7fbdbc3eab508020e15bd676fda6d667fcd7425690bce8e52421991
generated: "2026-05-22T19:10:09.883283-04:00"
12 changes: 12 additions & 0 deletions charts/infisical-secrets-operator/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v2
name: infisical-secrets-operator
description: Infisical Kubernetes operator — syncs platform secrets into native K8s Secrets (wraps secrets-operator chart)
type: application
version: 0.1.0
appVersion: "v0.10.33"
maintainers:
- name: homelab
dependencies:
- name: secrets-operator
version: 0.10.33
repository: https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: infisical-machine-identity
namespace: infisical-secrets-operator
spec:
encryptedData:
clientId: AgC7zFvkmaaxpaltQOSGMSPOb+pBZWap+F14kN9/rXrxWn5qxd84vUNXsLw0KKWKBvjNfrsjwFjLMhc6W8+vL3OrxAp1zq1WGhcHpvrfoyfBFRBCY30hNQbtkKmcWdruhvViVxkgXiwiroHo51tr4IFXO5FVV6graxowqHNnSTw6VpJt4GXQoQhrc4d2AfWDHU/3vNmvqUPod5o2SyEAr52JRgHHx7ELC1VoFfBxV513e9RezEiJMA2Ac7u9EFdjf7XV7usBIM2Pvk/5YhKGbqkzDQKz2+IL4AWgXfNz1Jr21RpqHZmCczIhj2JpqUXfJczGmLWgD/u7+OPYd5mU/HCCRn6HDRwrynPRWSIK/wlONwnrX36pY0hHhl5E+wPA54dWpP5oPxsOsYY2OeAz1c6DRcaFF9vOGWh3aaOL2haBzwdl/0mLwyruv2Cb6Krz2loLS1PG2SAuiv9A0x/pqexRq1WE/1D9SMGGAPICRK4p9IAHF1skuK5wNHs3j33OUqTsHUcbj+qkRr7o9TRv8o48oJw0NrodKPne+aIKro98PfQrE/S9mHz1cmTRnyJ1M2P0z0VR8SyXsBxhEQVfJ3LD/l1kIxQO4NFcEHWNLFMpKIuvtHfU0pZLlj9owLxedvxyqKIQiRLrNS3PgRdc9aQnmGR/iFBmDl5WXbh5AAa6qiRAGpFpX8TVlTClhtuwgExLT3xfzZSvkKbYmwlA5uixYFSMTcoxwAcSXGjuiox5bkXGGPk=
clientSecret: AgChVsgoorA99p2wVnMCxRiIUavmqnuU1wcMmJLDi4NI56ZoDdvEAiZBIC7T0WnLd5nXMD+a+L9xjd/2ZgqxbyvXGlUKsg9ba+SrdL1iLLr8by+iy/bWSL/3VbsX0KmxmrlTJtKxA2bWve/vzoPiWWKhQE/8GpDtnSPj9rxuq4NfrXi1oG6bx0G0yGCZ/EUAD7xY6Z54fTHHM11AQCkDL7upbH89OVeGIZPUy00mzjn9nj7boVKYe8zWLuCLsi1or9Y7hREA3pc8wbXODYBf0pISUaGNyqyqJcnBp9KyIt0CeBpems6fTCNNZEmUlKgXe9IkGPbB7cvoRML4xSEWsvuqjk5JwmpO8iTUFMLDNYeuSGMPYatGG9YJ6MJhocON4oIxnDecaAq9iZ2RhtSUSBHIF6QxSL6UGbSsqDULLtT2Exkch00V/asjWCmaVBed+VVAUVRpuK2vMlIvdYEh2d8dkxdjVe+UJTDQY2pk/Du3eMuQae+lHYbTV0QhycQDEeeNEXXDuIk7qmLSU61p0rWe5S416sCNevt/edMMeoeoGmV2wilnVaT1N/fBGGNz54QnlfmB7cqyhsgAF93hGF0FPJXX2jIzbWU2+WKLzPTtE1AqpeksDum02JK2S0qZU5qBcOIpbAVly7Uw0fjZFpzBVMKMFCUbgHvqKaPOvzJ6CqS0hKjlj+UvrsuIbsgNgNznVpkZHcOmrt9HgM1lV49eOpkECPalsG5lF/36QypVeb5F5mF7hHq5jmz9ktPJFt1uncHVIxBPK/pntiKSU8F9
template:
metadata:
name: infisical-machine-identity
namespace: infisical-secrets-operator
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Template for the Infisical machine-identity (Universal Auth) credentials.
#
# This is the single bootstrap secret the operator uses to authenticate to the
# self-hosted Infisical platform. Every InfisicalSecret CR references it via
# credentialsRef. Keep THIS file (placeholders) in git; commit the sealed
# output as infisical-machine-identity-sealed.yaml. Never commit real values.
#
# Steps:
# 1. Infisical UI -> Access Control -> Machine Identities -> create one with
# Universal Auth. Grant it read access to the project/env/path your
# InfisicalSecret CRs reference. Copy the Client ID + Client Secret.
# 2. Seal it into the operator namespace:
# kubectl create secret generic infisical-machine-identity \
# --namespace infisical-secrets-operator \
# --from-literal=clientId=<CLIENT_ID> \
# --from-literal=clientSecret=<CLIENT_SECRET> \
# --dry-run=client -o yaml | \
# make seal-secret CHART=infisical-secrets-operator SECRET=infisical-machine-identity
# 3. Commit charts/infisical-secrets-operator/sealed-secrets/infisical-machine-identity-sealed.yaml
---
apiVersion: v1
kind: Secret
metadata:
name: infisical-machine-identity
namespace: infisical-secrets-operator
type: Opaque
stringData:
clientId: REPLACE_WITH_CLIENT_ID
clientSecret: REPLACE_WITH_CLIENT_SECRET
12 changes: 12 additions & 0 deletions charts/infisical-secrets-operator/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Infisical Secrets Operator installed (namespace: {{ .Release.Namespace }}).

Next steps:
1. Create a machine identity in Infisical and seal its credentials:
charts/infisical-secrets-operator/sealed-secrets/infisical-machine-identity.template
2. Add an InfisicalSecret CR to a consuming chart (see charts/pihole).

Full runbook: docs/runbooks/INFISICAL_OPERATOR.md

Check the controller:
kubectl -n {{ .Release.Namespace }} get pods
kubectl get crd | grep infisical
42 changes: 42 additions & 0 deletions charts/infisical-secrets-operator/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
# Deployed to its own namespace (created via `helm install --create-namespace`).
namespace: infisical-secrets-operator

# Values passed through to the wrapped upstream chart (key = subchart name).
secrets-operator:
# Default Infisical host for every InfisicalSecret CR that omits its own
# hostAPI. Uses the in-cluster Service (charts/infisical) rather than the
# public ingress — that hostname only resolves via pihole's local DNS, which
# CoreDNS can't reach.
hostAPI: http://infisical-infisical-standalone-infisical.default.svc.cluster.local:8080/api

# Watch all namespaces (cluster-scoped). Apps' InfisicalSecret CRs live in
# their own namespaces (e.g. pihole in `default`), so a scoped operator
# would miss them.
scopedNamespaces: []

controllerManager:
replicas: 1
manager:
image:
repository: infisical/kubernetes-operator
tag: v0.10.33
resources:
requests:
cpu: 10m
memory: 64Mi
limits:
cpu: 500m
memory: 128Mi
# Prefer the Pi5 nodes (more memory) over the Pi4s.
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- pi5-01
- pi5-02
2 changes: 1 addition & 1 deletion charts/infisical/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: v2
name: infisical
description: Self-hosted secret management platform (wraps official infisical-standalone chart)
type: application
version: 1.1.0
version: 1.1.1
appVersion: "v0.158.0"
maintainers:
- name: homelab
Expand Down
4 changes: 3 additions & 1 deletion charts/infisical/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ namespace: default
postgres:
image:
repository: postgres
tag: "18"
# Pinned to 15 — matches existing on-disk data format. Major bumps need
# pg_upgrade (see authentik), so disabled in renovate.json.
tag: "15"
pullPolicy: IfNotPresent
storageClass: local-path
size: 8Gi
Expand Down
2 changes: 1 addition & 1 deletion charts/pihole/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: v2
name: pihole
description: Network-wide ad blocking via your own Linux hardware
type: application
version: 1.1.4
version: 1.1.5
appVersion: "2024.07.0"
keywords:
- dns
Expand Down
13 changes: 0 additions & 13 deletions charts/pihole/sealed-secrets/pihole-secrets-sealed.yaml

This file was deleted.

Loading
Loading