Initial argo deployment
This commit is contained in:
136
cluster/argocd/BOOTSTRAP.md
Normal file
136
cluster/argocd/BOOTSTRAP.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# fastpass GitOps Bootstrap
|
||||
|
||||
This document is the complete procedure to go from a freshly provisioned Talos
|
||||
cluster to a fully GitOps-managed platform. After completing these steps, all
|
||||
future changes are made via git — never `kubectl apply` again.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Talos cluster running (`space-mountain`, `big-thunder-mountain`, `splash-mountain` CP nodes)
|
||||
- Cilium installed as CNI (done at Talos bootstrap via talhelper)
|
||||
- `kubectl` configured and pointing at `fastpass`
|
||||
- `kubeconfig` on `city-hall` at `~/.kube/config`
|
||||
- 1Password Connect credentials JSON downloaded from your 1Password account
|
||||
(Developer Tools > Connect Servers > New Server)
|
||||
- 1Password Connect token generated for the `mk-labs` vault
|
||||
|
||||
## Step 1 — Install ArgoCD
|
||||
|
||||
This is the only `kubectl apply` you will ever run against this cluster.
|
||||
|
||||
```bash
|
||||
kubectl create namespace argocd
|
||||
|
||||
kubectl apply -n argocd \
|
||||
--server-side \
|
||||
--force-conflicts \
|
||||
-f https://raw.githubusercontent.com/argoproj/argo-cd/v3.4.2/manifests/install.yaml
|
||||
|
||||
# Wait for ArgoCD to be healthy
|
||||
kubectl wait --for=condition=available deployment/argocd-server \
|
||||
-n argocd --timeout=120s
|
||||
```
|
||||
|
||||
## Step 2 — Bootstrap the 1Password Connect secret
|
||||
|
||||
This is the only secret you will ever manage manually. Everything else flows
|
||||
through ESO from here.
|
||||
|
||||
```bash
|
||||
# Create the namespace first
|
||||
kubectl create namespace onepassword-connect
|
||||
|
||||
# Apply the credentials file (downloaded from 1Password)
|
||||
kubectl create secret generic op-credentials \
|
||||
--from-file=1password-credentials.json=/path/to/1password-credentials.json \
|
||||
-n onepassword-connect
|
||||
|
||||
# Apply the Connect API token
|
||||
kubectl create secret generic connect-token \
|
||||
--from-literal=token=<your-connect-token> \
|
||||
-n onepassword-connect
|
||||
```
|
||||
|
||||
## Step 3 — Apply the App of Apps
|
||||
|
||||
This is the second and final manual apply. From this point on, ArgoCD manages
|
||||
everything, including itself.
|
||||
|
||||
```bash
|
||||
kubectl apply -n argocd -f cluster/argocd/apps-of-apps.yaml
|
||||
```
|
||||
|
||||
## Step 4 — Watch it go
|
||||
|
||||
```bash
|
||||
# Watch all Applications come up in wave order
|
||||
kubectl get applications -n argocd -w
|
||||
|
||||
# Or use the ArgoCD UI
|
||||
kubectl port-forward svc/argocd-server -n argocd 8080:443
|
||||
# Then open https://localhost:8080
|
||||
# Default admin password:
|
||||
kubectl -n argocd get secret argocd-initial-admin-secret \
|
||||
-o jsonpath="{.data.password}" | base64 -d
|
||||
```
|
||||
|
||||
## Step 2.5 — Vendor Gateway API CRDs
|
||||
|
||||
Before applying the App of Apps, vendor the Gateway API CRDs into git.
|
||||
Run from the repo root on city-hall:
|
||||
|
||||
```bash
|
||||
curl -L https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml \
|
||||
-o cluster/platform/gateway-api/manifests/standard-install.yaml
|
||||
|
||||
curl -L https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/experimental-install.yaml \
|
||||
-o cluster/platform/gateway-api/manifests/experimental-install.yaml
|
||||
|
||||
git add cluster/platform/gateway-api/manifests/
|
||||
git commit -m "feat(platform): vendor gateway-api CRDs v1.2.1"
|
||||
git push
|
||||
```
|
||||
|
||||
## Wave Order
|
||||
|
||||
| Wave | Application | Depends On |
|
||||
|------|---------------------|---------------------------------|
|
||||
| 1 | external-secrets | (none) |
|
||||
| 2 | onepassword-connect | external-secrets |
|
||||
| 3 | cert-manager | onepassword-connect (CF secret) |
|
||||
| 3 | gateway-api-crds | (none — CRDs only) |
|
||||
| 4 | cilium-config | gateway-api-crds (CRDs exist) |
|
||||
| 5 | ingress-nginx | cilium-config (LB pool) |
|
||||
| 5 | gateway (resource) | cilium-config (GatewayClass) |
|
||||
| 6 | external-dns | ingress-nginx (IP to register) |
|
||||
| 10+ | applications/* | platform complete |
|
||||
|
||||
## Upgrading ArgoCD
|
||||
|
||||
```bash
|
||||
kubectl apply -n argocd \
|
||||
--server-side \
|
||||
--force-conflicts \
|
||||
-f https://raw.githubusercontent.com/argoproj/argo-cd/v<NEW_VERSION>/manifests/install.yaml
|
||||
```
|
||||
|
||||
Update the version comment in `cluster/argocd/install.yaml` to match.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
```bash
|
||||
# Check sync status of all apps
|
||||
kubectl get applications -n argocd
|
||||
|
||||
# Check why an app is out of sync
|
||||
kubectl describe application <name> -n argocd
|
||||
|
||||
# Force a sync
|
||||
kubectl patch application <name> -n argocd \
|
||||
--type merge \
|
||||
-p '{"operation": {"initiatedBy": {"username": "admin"}, "sync": {}}}'
|
||||
|
||||
# Check ESO secret sync
|
||||
kubectl get externalsecret -A
|
||||
kubectl describe externalsecret <name> -n <namespace>
|
||||
```
|
||||
45
cluster/argocd/apps-of-apps.yaml
Normal file
45
cluster/argocd/apps-of-apps.yaml
Normal file
@@ -0,0 +1,45 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# App of Apps — Root
|
||||
#
|
||||
# This is applied ONCE after ArgoCD is bootstrapped. It is the second and
|
||||
# final manual kubectl apply:
|
||||
#
|
||||
# kubectl apply -n argocd -f cluster/argocd/apps-of-apps.yaml
|
||||
#
|
||||
# From this point forward, all changes are made via git. ArgoCD self-manages.
|
||||
#
|
||||
# This Application watches cluster/platform/ and cluster/applications/ and
|
||||
# creates child Applications for everything it finds there.
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: apps-of-apps
|
||||
namespace: argocd
|
||||
annotations:
|
||||
# Wave 0 — this is the root, everything else comes after
|
||||
argocd.argoproj.io/sync-wave: "0"
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://gitea.mk-labs.cloud/rblundon/homelab.git
|
||||
targetRevision: main
|
||||
path: cluster
|
||||
directory:
|
||||
recurse: true
|
||||
include: "*/application.yaml"
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: argocd
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
ignoreDifferences:
|
||||
- group: argoproj.io
|
||||
kind: Application
|
||||
jsonPointers:
|
||||
- /spec/syncPolicy/automated
|
||||
21
cluster/argocd/install.yaml
Normal file
21
cluster/argocd/install.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# ArgoCD Install — v3.4.2
|
||||
#
|
||||
# This is NOT applied by ArgoCD itself (chicken-and-egg).
|
||||
# It is the ONE manual bootstrap step:
|
||||
#
|
||||
# kubectl create namespace argocd
|
||||
# kubectl apply -n argocd --server-side --force-conflicts -f install.yaml
|
||||
#
|
||||
# After this, everything else is driven by apps-of-apps.yaml.
|
||||
#
|
||||
# To upgrade ArgoCD: update the version below and re-apply.
|
||||
# --server-side --force-conflicts is required due to CRD size limits.
|
||||
#
|
||||
# Source: https://github.com/argoproj/argo-cd/releases/tag/v3.4.2
|
||||
# ------------------------------------------------------------------------------
|
||||
# kubectl apply -n argocd --server-side --force-conflicts -f \
|
||||
# https://raw.githubusercontent.com/argoproj/argo-cd/v3.4.2/manifests/install.yaml
|
||||
#
|
||||
# We reference the upstream manifest directly rather than vendoring it.
|
||||
# Pin upgrades by changing the version tag above and re-running the command.
|
||||
4
cluster/argocd/namespace.yaml
Normal file
4
cluster/argocd/namespace.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: argocd
|
||||
44
cluster/platform/argocd/application.yaml
Normal file
44
cluster/platform/argocd/application.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Platform: ArgoCD Configuration (post-bootstrap)
|
||||
# Wave 2 — after ESO (wave 1), needs 1Password for Authentik client secret
|
||||
#
|
||||
# This is NOT the ArgoCD install (that's a manual bootstrap step).
|
||||
# This Application manages ArgoCD's own configuration declaratively:
|
||||
# - argocd-cm (URL, Dex/OIDC config, repo credentials)
|
||||
# - argocd-rbac-cm (RBAC policies mapped from Authentik groups)
|
||||
# - argocd-secret (Authentik client secret via ESO)
|
||||
#
|
||||
# ArgoCD managing its own config is intentional and works cleanly —
|
||||
# it's the "self-managed ArgoCD" pattern.
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: argocd-config
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "2"
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://gitea.mk-labs.cloud/rblundon/homelab.git
|
||||
targetRevision: main
|
||||
path: cluster/platform/argocd
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: argocd
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- ServerSideApply=true
|
||||
ignoreDifferences:
|
||||
# ArgoCD mutates its own secret — ignore those fields
|
||||
- group: ""
|
||||
kind: Secret
|
||||
name: argocd-secret
|
||||
jsonPointers:
|
||||
- /data/admin.password
|
||||
- /data/admin.passwordMtime
|
||||
- /data/server.secretkey
|
||||
69
cluster/platform/argocd/argocd-cm.yaml
Normal file
69
cluster/platform/argocd/argocd-cm.yaml
Normal file
@@ -0,0 +1,69 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# argocd-cm — ArgoCD Core Configuration
|
||||
#
|
||||
# Configures:
|
||||
# - ArgoCD public URL (used in Dex redirect URIs)
|
||||
# - Dex connector to Authentik OIDC
|
||||
# - Gitea repo credentials reference
|
||||
#
|
||||
# Authentik side setup (manual — do this in guest-relations before bootstrapping):
|
||||
# 1. Admin UI → Applications → Create New Application
|
||||
# 2. Provider type: OAuth2/OpenID Connect
|
||||
# 3. Name: argocd
|
||||
# 4. Authorization flow: default-provider-authorization-explicit-consent
|
||||
# 5. Client type: Confidential
|
||||
# 6. Strict redirect URIs:
|
||||
# https://argocd.local.mk-labs.cloud/api/dex/callback
|
||||
# http://localhost:8085/auth/callback (ArgoCD CLI)
|
||||
# 7. Scopes: openid, profile, email, groups
|
||||
# 8. Note the Client ID and Client Secret → store in 1Password as:
|
||||
# Item: "argocd-oidc"
|
||||
# Fields: client-id, client-secret
|
||||
# 9. Create two groups in Authentik:
|
||||
# - argocd-admins (add your user)
|
||||
# - argocd-viewers (read-only users)
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-cm
|
||||
namespace: argocd
|
||||
data:
|
||||
# Public URL — must match the HTTPRoute hostname
|
||||
url: https://argocd.local.mk-labs.cloud
|
||||
|
||||
# Dex connector to Authentik
|
||||
# Using Dex (not native OIDC) to preserve ArgoCD CLI functionality
|
||||
dex.config: |
|
||||
connectors:
|
||||
- type: oidc
|
||||
id: authentik
|
||||
name: Authentik
|
||||
config:
|
||||
# Authentik OIDC issuer — slug must match the Application slug in Authentik
|
||||
# Slug is auto-generated from the app name, usually lowercase with hyphens
|
||||
issuer: https://authentik.local.mk-labs.cloud/application/o/argocd/
|
||||
clientID: $dex.authentik.clientID
|
||||
clientSecret: $dex.authentik.clientSecret
|
||||
redirectURI: https://argocd.local.mk-labs.cloud/api/dex/callback
|
||||
scopes:
|
||||
- openid
|
||||
- profile
|
||||
- email
|
||||
- groups
|
||||
insecureEnableGroups: true
|
||||
groupsKey: groups
|
||||
userIDKey: sub
|
||||
userNameKey: preferred_username
|
||||
emailKey: email
|
||||
|
||||
# Gitea repo — ArgoCD needs credentials to pull from your private repo
|
||||
# Credentials are stored as a Repository secret (see repository-secret.yaml)
|
||||
# This tells ArgoCD to use SSH for the repo
|
||||
repositories: |
|
||||
- url: git@gitea.mk-labs.cloud:rblundon/homelab.git
|
||||
type: git
|
||||
name: homelab
|
||||
|
||||
# Resource tracking via annotation (recommended over label tracking)
|
||||
application.resourceTrackingMethod: annotation
|
||||
30
cluster/platform/argocd/argocd-rbac-cm.yaml
Normal file
30
cluster/platform/argocd/argocd-rbac-cm.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# argocd-rbac-cm — RBAC Policy
|
||||
#
|
||||
# Maps Authentik groups to ArgoCD roles.
|
||||
# Groups must exist in Authentik and be assigned to users there.
|
||||
#
|
||||
# Roles:
|
||||
# role:admin — full access, all operations
|
||||
# role:readonly — read-only, can view but not sync/modify
|
||||
#
|
||||
# Single-user homelab: your user should be in argocd-admins in Authentik.
|
||||
# Add argocd-viewers later when you have guests/collaborators.
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: argocd-rbac-cm
|
||||
namespace: argocd
|
||||
data:
|
||||
# Default policy for any authenticated user not in a mapped group
|
||||
# readonly is safe — they can see but not touch
|
||||
policy.default: role:readonly
|
||||
|
||||
policy.csv: |
|
||||
# Authentik group → ArgoCD role mappings
|
||||
g, argocd-admins, role:admin
|
||||
g, argocd-viewers, role:readonly
|
||||
|
||||
# Tell ArgoCD which OIDC claims to use for group mapping
|
||||
scopes: '[groups, email]'
|
||||
23
cluster/platform/argocd/argocd-server-patch.yaml
Normal file
23
cluster/platform/argocd/argocd-server-patch.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# ArgoCD Server — Insecure Mode Patch
|
||||
#
|
||||
# When TLS is terminated at the Gateway (not at argocd-server itself),
|
||||
# argocd-server must run in --insecure mode. Without this, it tries to
|
||||
# do double TLS and the Gateway connection fails.
|
||||
#
|
||||
# This patches the argocd-server Deployment to add the --insecure flag.
|
||||
# Applied as part of the argocd-config Application (same path/namespace).
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: argocd-server
|
||||
namespace: argocd
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: argocd-server
|
||||
command:
|
||||
- argocd-server
|
||||
- --insecure
|
||||
41
cluster/platform/argocd/certificate.yaml
Normal file
41
cluster/platform/argocd/certificate.yaml
Normal file
@@ -0,0 +1,41 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Certificate — ArgoCD TLS
|
||||
# Issued by cert-manager via letsencrypt-prod + Cloudflare DNS-01
|
||||
#
|
||||
# The secret produced (argocd-tls) is referenced by the Gateway listener
|
||||
# in cluster/platform/gateway-api/gateway.yaml via ReferenceGrant.
|
||||
#
|
||||
# Per the thematic naming convention: argocd.local.mk-labs.cloud is the
|
||||
# service DNS name. No VM hostname alias needed here.
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: argocd-tls
|
||||
namespace: argocd
|
||||
spec:
|
||||
secretName: argocd-tls
|
||||
issuerRef:
|
||||
name: letsencrypt-prod
|
||||
kind: ClusterIssuer
|
||||
dnsNames:
|
||||
- argocd.local.mk-labs.cloud
|
||||
|
||||
---
|
||||
# ReferenceGrant — allows the Gateway in gateway-system to reference
|
||||
# the TLS secret in the argocd namespace
|
||||
# Without this, cross-namespace secret references are denied
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
kind: ReferenceGrant
|
||||
metadata:
|
||||
name: argocd-tls-grant
|
||||
namespace: argocd
|
||||
spec:
|
||||
from:
|
||||
- group: gateway.networking.k8s.io
|
||||
kind: Gateway
|
||||
namespace: gateway-system
|
||||
to:
|
||||
- group: ""
|
||||
kind: Secret
|
||||
name: argocd-tls
|
||||
49
cluster/platform/argocd/externalsecret.yaml
Normal file
49
cluster/platform/argocd/externalsecret.yaml
Normal file
@@ -0,0 +1,49 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# ExternalSecret — ArgoCD Secrets
|
||||
#
|
||||
# Pulls two secrets from 1Password:
|
||||
# 1. Authentik OIDC client ID + secret (for Dex connector)
|
||||
# 2. Gitea personal access token (for repo access)
|
||||
#
|
||||
# In 1Password (mk-labs vault), create:
|
||||
# Item: "argocd-oidc"
|
||||
# Field: client-id → Authentik OAuth2 client ID
|
||||
# Field: client-secret → Authentik OAuth2 client secret
|
||||
#
|
||||
# Item: "gitea-argocd-token"
|
||||
# Field: token → Gitea personal access token with repo read permissions
|
||||
# (Create in Gitea: Settings → Applications → Generate Token)
|
||||
#
|
||||
# These materialize into argocd-secret, which ArgoCD reads natively.
|
||||
# The $dex.authentik.clientID / $dex.authentik.clientSecret references
|
||||
# in argocd-cm.yaml are resolved by ArgoCD from this secret automatically.
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: argocd-secrets
|
||||
namespace: argocd
|
||||
spec:
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: onepassword-connect
|
||||
refreshInterval: "1h"
|
||||
target:
|
||||
# Must be named argocd-secret — ArgoCD reads this specific secret name
|
||||
name: argocd-secret
|
||||
creationPolicy: Merge # Merge into existing argocd-secret, don't replace it
|
||||
data:
|
||||
# Authentik OIDC credentials — referenced as $dex.authentik.* in argocd-cm
|
||||
- secretKey: dex.authentik.clientID
|
||||
remoteRef:
|
||||
key: argocd-oidc
|
||||
property: client-id
|
||||
- secretKey: dex.authentik.clientSecret
|
||||
remoteRef:
|
||||
key: argocd-oidc
|
||||
property: client-secret
|
||||
# Gitea token — used by the Repository credential below
|
||||
- secretKey: sshPrivateKey
|
||||
remoteRef:
|
||||
key: gitea-argocd-ssh
|
||||
property: private-key
|
||||
54
cluster/platform/argocd/httproute.yaml
Normal file
54
cluster/platform/argocd/httproute.yaml
Normal file
@@ -0,0 +1,54 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# HTTPRoute — ArgoCD
|
||||
# Routes argocd.local.mk-labs.cloud → argocd-server service
|
||||
#
|
||||
# This is the first real Gateway API HTTPRoute in the cluster — ArgoCD
|
||||
# dogfoods the Gateway it manages. Satisfying.
|
||||
#
|
||||
# TLS is terminated at the Gateway. cert-manager issues the certificate
|
||||
# via the Certificate resource below. The Gateway listener references it.
|
||||
#
|
||||
# ArgoCD server runs in insecure mode (--insecure) when TLS is terminated
|
||||
# upstream — this is correct and expected with a Gateway/proxy in front.
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: argocd
|
||||
namespace: argocd
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: fastpass-gateway
|
||||
namespace: gateway-system
|
||||
sectionName: https
|
||||
hostnames:
|
||||
- argocd.local.mk-labs.cloud
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: argocd-server
|
||||
port: 80 # argocd-server listens on 80 in insecure mode
|
||||
|
||||
---
|
||||
# HTTP → HTTPS redirect
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: argocd-http-redirect
|
||||
namespace: argocd
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: fastpass-gateway
|
||||
namespace: gateway-system
|
||||
sectionName: http
|
||||
hostnames:
|
||||
- argocd.local.mk-labs.cloud
|
||||
rules:
|
||||
- filters:
|
||||
- type: RequestRedirect
|
||||
requestRedirect:
|
||||
scheme: https
|
||||
statusCode: 301
|
||||
26
cluster/platform/argocd/repository-secret.yaml
Normal file
26
cluster/platform/argocd/repository-secret.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Repository Credential Secret — Gitea homelab repo (SSH)
|
||||
#
|
||||
# ArgoCD discovers repository credentials via secrets with the label
|
||||
# argocd.argoproj.io/secret-type: repository
|
||||
#
|
||||
# The SSH private key is injected by ESO from 1Password.
|
||||
# In 1Password (mk-labs vault):
|
||||
# Item: "gitea-argocd-ssh"
|
||||
# Field: private-key → full contents of the ed25519 private key PEM block
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: repo-homelab-gitea
|
||||
namespace: argocd
|
||||
labels:
|
||||
argocd.argoproj.io/secret-type: repository
|
||||
stringData:
|
||||
type: git
|
||||
url: git@gitea.mk-labs.cloud:rblundon/homelab.git
|
||||
sshPrivateKey: |
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
# This value is overwritten by ESO — placeholder only
|
||||
# See externalsecret.yaml
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
29
cluster/platform/cert-manager/application.yaml
Normal file
29
cluster/platform/cert-manager/application.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Platform: cert-manager
|
||||
# Wave 3 — depends on ESO (wave 1) for Cloudflare token secret
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: cert-manager
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "3"
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://charts.jetstack.io
|
||||
chart: cert-manager
|
||||
targetRevision: v1.17.2
|
||||
helm:
|
||||
valuesFile: values.yaml
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: cert-manager
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
49
cluster/platform/cert-manager/clusterissuer.yaml
Normal file
49
cluster/platform/cert-manager/clusterissuer.yaml
Normal file
@@ -0,0 +1,49 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# cert-manager ClusterIssuer — Let's Encrypt via Cloudflare DNS-01
|
||||
#
|
||||
# The cloudflare-api-token secret is populated by ESO from 1Password.
|
||||
# See externalsecret.yaml for the ESO resource that creates it.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Staging issuer — use this first to avoid LE rate limits during testing
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: letsencrypt-staging
|
||||
spec:
|
||||
acme:
|
||||
server: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
email: ryan.blundon@protonmail.com
|
||||
privateKeySecretRef:
|
||||
name: letsencrypt-staging-key
|
||||
solvers:
|
||||
- dns01:
|
||||
cloudflare:
|
||||
apiTokenSecretRef:
|
||||
name: cloudflare-api-token
|
||||
key: api-token
|
||||
selector:
|
||||
dnsZones:
|
||||
- mk-labs.cloud
|
||||
|
||||
---
|
||||
# Production issuer — switch to this once staging certs are working
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: letsencrypt-prod
|
||||
spec:
|
||||
acme:
|
||||
server: https://acme-v02.api.letsencrypt.org/directory
|
||||
email: ryan.blundon@protonmail.com
|
||||
privateKeySecretRef:
|
||||
name: letsencrypt-prod-key
|
||||
solvers:
|
||||
- dns01:
|
||||
cloudflare:
|
||||
apiTokenSecretRef:
|
||||
name: cloudflare-api-token
|
||||
key: api-token
|
||||
selector:
|
||||
dnsZones:
|
||||
- mk-labs.cloud
|
||||
26
cluster/platform/cert-manager/externalsecret.yaml
Normal file
26
cluster/platform/cert-manager/externalsecret.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# ExternalSecret — Cloudflare API Token
|
||||
# Pulled from 1Password via Connect Server, materialized as a K8s secret
|
||||
# in the cert-manager namespace for use by the ClusterIssuer.
|
||||
#
|
||||
# In 1Password, store the token as an item named "cloudflare" with a field
|
||||
# named "api-token" in the "mk-labs" vault.
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: cloudflare-api-token
|
||||
namespace: cert-manager
|
||||
spec:
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: onepassword-connect
|
||||
refreshInterval: "1h"
|
||||
target:
|
||||
name: cloudflare-api-token
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: api-token
|
||||
remoteRef:
|
||||
key: cloudflare # 1Password item name
|
||||
property: api-token # 1Password field name
|
||||
23
cluster/platform/cert-manager/values.yaml
Normal file
23
cluster/platform/cert-manager/values.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# cert-manager — Helm Values
|
||||
# Chart: https://charts.jetstack.io
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Install CRDs via Helm (recommended — keeps CRDs in sync with chart version)
|
||||
crds:
|
||||
enabled: true
|
||||
|
||||
# Replicas — 1 is fine for homelab
|
||||
replicaCount: 1
|
||||
|
||||
# Use Cloudflare DNS-01 recursive nameservers (same pattern as Traefik/lightning-lane)
|
||||
# Only use 1.1.1.1 for DNS-01 challenge resolution — keeps it off internal DNS
|
||||
extraArgs:
|
||||
- --dns01-recursive-nameservers-only
|
||||
- --dns01-recursive-nameservers=1.1.1.1:53
|
||||
|
||||
# Prometheus metrics for cinderella-castle
|
||||
prometheus:
|
||||
enabled: true
|
||||
servicemonitor:
|
||||
enabled: false # enable once Prometheus operator is in-cluster
|
||||
48
cluster/platform/cilium-config/application.yaml
Normal file
48
cluster/platform/cilium-config/application.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Platform: Cilium — Helm upgrade + LB IPAM + Gateway API
|
||||
# Wave 4 — no dependencies, Cilium is pre-installed via Talos bootstrap
|
||||
#
|
||||
# This Application does two things:
|
||||
# 1. Manages Cilium itself via Helm (enables Gateway API, keeps config in git)
|
||||
# 2. Applies LB pool and L2 announcement manifests
|
||||
#
|
||||
# On first sync, ArgoCD will upgrade Cilium in-place with --reuse-values
|
||||
# plus our additions (gatewayAPI.enabled, l2announcements, etc.).
|
||||
# Cilium operator and daemonset will rolling-restart automatically.
|
||||
#
|
||||
# IP Allocation:
|
||||
# 10.1.71.80 — ingress-nginx (reserved, not from pool)
|
||||
# 10.1.71.81 — Cilium Gateway (reserved, not from pool)
|
||||
# 10.1.71.82-.89 — ingress-nginx LB pool (future additional controllers)
|
||||
# 10.1.71.90-.99 — Cilium Gateway LB pool
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: cilium-config
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "4"
|
||||
spec:
|
||||
project: default
|
||||
sources:
|
||||
# Source 1: Cilium Helm chart — manages Cilium itself
|
||||
- repoURL: https://helm.cilium.io
|
||||
chart: cilium
|
||||
targetRevision: 1.17.4
|
||||
helm:
|
||||
valuesFiles:
|
||||
- $values/cluster/platform/cilium-config/values.yaml
|
||||
# Source 2: Our repo — provides the values file and manifests
|
||||
- repoURL: https://gitea.mk-labs.cloud/rblundon/homelab.git
|
||||
targetRevision: main
|
||||
ref: values
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: kube-system
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- ServerSideApply=true
|
||||
66
cluster/platform/cilium-config/manifests/lb-pool.yaml
Normal file
66
cluster/platform/cilium-config/manifests/lb-pool.yaml
Normal file
@@ -0,0 +1,66 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Cilium LB IPAM — IP Address Pools
|
||||
#
|
||||
# Pool 1: ingress-nginx pool — 10.1.71.80 - 10.1.71.89
|
||||
# Pool 2: cilium-gateway pool — 10.1.71.90 - 10.1.71.99
|
||||
#
|
||||
# Pools are separated so ingress-nginx and Gateway API services never
|
||||
# compete for the same IPs. Each pool uses a serviceSelector to ensure
|
||||
# IPs are only assigned to the right controller.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Pool for ingress-nginx LoadBalancer service
|
||||
apiVersion: cilium.io/v2alpha1
|
||||
kind: CiliumLoadBalancerIPPool
|
||||
metadata:
|
||||
name: ingress-nginx-pool
|
||||
namespace: kube-system
|
||||
spec:
|
||||
blocks:
|
||||
- start: 10.1.71.80
|
||||
stop: 10.1.71.89
|
||||
serviceSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: ingress-nginx
|
||||
|
||||
---
|
||||
# Pool for Cilium Gateway API — only assigned to Gateway-owned services
|
||||
# Cilium automatically labels Gateway services with io.cilium.gateway/owning-gateway
|
||||
apiVersion: cilium.io/v2alpha1
|
||||
kind: CiliumLoadBalancerIPPool
|
||||
metadata:
|
||||
name: cilium-gateway-pool
|
||||
namespace: kube-system
|
||||
spec:
|
||||
blocks:
|
||||
- start: 10.1.71.90
|
||||
stop: 10.1.71.99
|
||||
serviceSelector:
|
||||
matchExpressions:
|
||||
- key: io.cilium.gateway/owning-gateway
|
||||
operator: Exists
|
||||
|
||||
---
|
||||
# L2 Announcement Policy — ARP on server VLAN for both pools
|
||||
apiVersion: cilium.io/v2alpha1
|
||||
kind: CiliumL2AnnouncementPolicy
|
||||
metadata:
|
||||
name: fastpass-l2-policy
|
||||
namespace: kube-system
|
||||
spec:
|
||||
loadBalancerIPs: true
|
||||
# All nodes participate — Cilium handles failover automatically
|
||||
interfaces:
|
||||
- ^eth[0-9]+
|
||||
|
||||
---
|
||||
# GatewayClass — tells Kubernetes that Cilium handles Gateway resources
|
||||
# This is the entry point for all Gateway API routing
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: GatewayClass
|
||||
metadata:
|
||||
name: cilium
|
||||
spec:
|
||||
controllerName: io.cilium/gateway-controller
|
||||
description: Cilium Gateway API implementation (eBPF-native)
|
||||
|
||||
57
cluster/platform/cilium-config/values.yaml
Normal file
57
cluster/platform/cilium-config/values.yaml
Normal file
@@ -0,0 +1,57 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Cilium Helm Values — fastpass cluster
|
||||
# Chart: https://helm.cilium.io
|
||||
# Version: 1.17.4
|
||||
#
|
||||
# These values are applied as a Helm upgrade over the bootstrap install.
|
||||
# --reuse-values is implicit via ArgoCD's Helm source — existing values
|
||||
# from the Talos bootstrap are preserved; we only override what's listed here.
|
||||
#
|
||||
# Talos bootstrap values (already set, do not need repeating):
|
||||
# kubeProxyReplacement: true
|
||||
# k8sServiceHost: 10.1.71.65
|
||||
# k8sServicePort: 6443
|
||||
# ipam.mode: kubernetes
|
||||
# tunnel: vxlan
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Enable Gateway API support — this is the key addition
|
||||
# Cilium operator will register as a GatewayClass controller automatically
|
||||
gatewayAPI:
|
||||
enabled: true
|
||||
|
||||
# L2 announcements — ARP-based LB IP advertisement on the server VLAN
|
||||
l2announcements:
|
||||
enabled: true
|
||||
|
||||
# Rate limiting for L2 announcements — important with many services
|
||||
# Tune up if you see "rate limit exceeded" in cilium-operator logs
|
||||
k8sClientRateLimit:
|
||||
qps: 20
|
||||
burst: 40
|
||||
|
||||
# ExternalIPs advertisement
|
||||
externalIPs:
|
||||
enabled: true
|
||||
|
||||
# Hubble observability — worth enabling for visibility into cluster traffic
|
||||
hubble:
|
||||
enabled: true
|
||||
relay:
|
||||
enabled: true
|
||||
ui:
|
||||
enabled: true
|
||||
metrics:
|
||||
enabled:
|
||||
- dns
|
||||
- drop
|
||||
- tcp
|
||||
- flow
|
||||
- port-distribution
|
||||
- httpV2
|
||||
|
||||
# Prometheus metrics
|
||||
prometheus:
|
||||
enabled: true
|
||||
serviceMonitor:
|
||||
enabled: false # enable once Prometheus operator is in-cluster
|
||||
30
cluster/platform/external-dns/application.yaml
Normal file
30
cluster/platform/external-dns/application.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Platform: external-dns
|
||||
# Wave 6 — depends on ingress-nginx (wave 5) to have an IP to register
|
||||
# Configured to sync with Technitium DNS on monorail (10.1.71.32)
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: external-dns
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "6"
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://kubernetes-sigs.github.io/external-dns
|
||||
chart: external-dns
|
||||
targetRevision: 1.15.1
|
||||
helm:
|
||||
valuesFile: values.yaml
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: external-dns
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
29
cluster/platform/external-dns/externalsecret.yaml
Normal file
29
cluster/platform/external-dns/externalsecret.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# ExternalSecret — Technitium TSIG Secret
|
||||
# Pulled from 1Password, materialized in the external-dns namespace.
|
||||
#
|
||||
# In 1Password, store as item "technitium-tsig" with field "tsig-secret"
|
||||
# in the "mk-labs" vault.
|
||||
#
|
||||
# To generate a TSIG key for Technitium:
|
||||
# tsig-keygen -a hmac-sha256 external-dns
|
||||
# Add the key to Technitium under Settings > DNS Settings > TSIG Keys
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: technitium-tsig-secret
|
||||
namespace: external-dns
|
||||
spec:
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: onepassword-connect
|
||||
refreshInterval: "1h"
|
||||
target:
|
||||
name: technitium-tsig-secret
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: tsig-secret
|
||||
remoteRef:
|
||||
key: technitium-tsig # 1Password item name
|
||||
property: tsig-secret # 1Password field name
|
||||
58
cluster/platform/external-dns/values.yaml
Normal file
58
cluster/platform/external-dns/values.yaml
Normal file
@@ -0,0 +1,58 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# external-dns — Helm Values
|
||||
# Chart: https://kubernetes-sigs.github.io/external-dns
|
||||
#
|
||||
# Provider: Technitium DNS on monorail (10.1.71.32)
|
||||
# Technitium uses the RFC2136 (DNS UPDATE) provider in external-dns.
|
||||
# Zone: local.mk-labs.cloud
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
provider:
|
||||
name: rfc2136
|
||||
|
||||
env:
|
||||
# Technitium API endpoint — RFC2136 compatible
|
||||
- name: EXTERNAL_DNS_RFC2136_HOST
|
||||
value: "10.1.71.32"
|
||||
- name: EXTERNAL_DNS_RFC2136_PORT
|
||||
value: "53"
|
||||
- name: EXTERNAL_DNS_RFC2136_ZONE
|
||||
value: "local.mk-labs.cloud"
|
||||
- name: EXTERNAL_DNS_RFC2136_TSIG_AXFR
|
||||
value: "true"
|
||||
# TSIG secret key name — must match what's configured in Technitium
|
||||
- name: EXTERNAL_DNS_RFC2136_TSIG_SECRET_ALG
|
||||
value: "hmac-sha256"
|
||||
- name: EXTERNAL_DNS_RFC2136_TSIG_KEYNAME
|
||||
value: "external-dns"
|
||||
- name: EXTERNAL_DNS_RFC2136_TSIG_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: technitium-tsig-secret
|
||||
key: tsig-secret
|
||||
|
||||
# Only manage records in this zone
|
||||
domainFilters:
|
||||
- local.mk-labs.cloud
|
||||
|
||||
# Only process Ingress resources (and Services with the right annotation)
|
||||
sources:
|
||||
- ingress
|
||||
- service
|
||||
|
||||
# Annotation to opt services into DNS management:
|
||||
# external-dns.alpha.kubernetes.io/hostname: myservice.local.mk-labs.cloud
|
||||
annotationFilter: "external-dns.alpha.kubernetes.io/hostname"
|
||||
|
||||
# Log level — info in normal ops, debug when troubleshooting
|
||||
logLevel: info
|
||||
|
||||
# Interval between sync runs
|
||||
interval: "1m"
|
||||
|
||||
# Policy: sync = create AND delete records. upsert-only = never delete.
|
||||
policy: sync
|
||||
|
||||
# Registry to track which records external-dns owns
|
||||
registry: txt
|
||||
txtOwnerId: fastpass
|
||||
29
cluster/platform/external-secrets/application.yaml
Normal file
29
cluster/platform/external-secrets/application.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Platform: External Secrets Operator
|
||||
# Wave 1 — must be fully healthy before 1Password Connect (wave 2)
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: external-secrets
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "1"
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://charts.external-secrets.io
|
||||
chart: external-secrets
|
||||
targetRevision: 0.14.4
|
||||
helm:
|
||||
valuesFile: values.yaml
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: external-secrets
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
23
cluster/platform/external-secrets/values.yaml
Normal file
23
cluster/platform/external-secrets/values.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# External Secrets Operator — Helm Values
|
||||
# Chart: https://charts.external-secrets.io
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Install CRDs as part of the chart (recommended for GitOps)
|
||||
installCRDs: true
|
||||
|
||||
# Replica count — 1 is fine for homelab
|
||||
replicaCount: 1
|
||||
|
||||
# Enable Prometheus metrics for cinderella-castle monitoring
|
||||
metrics:
|
||||
service:
|
||||
enabled: true
|
||||
|
||||
# Webhook for secret validation
|
||||
webhook:
|
||||
replicaCount: 1
|
||||
|
||||
# Cert controller manages webhook certs
|
||||
certController:
|
||||
replicaCount: 1
|
||||
40
cluster/platform/gateway-api/application.yaml
Normal file
40
cluster/platform/gateway-api/application.yaml
Normal file
@@ -0,0 +1,40 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Platform: Gateway API CRDs
|
||||
# Wave 3.5 — must exist before cilium-config (wave 4) enables gatewayAPI
|
||||
#
|
||||
# The Gateway API CRDs are maintained separately from any implementation.
|
||||
# We install the standard channel CRDs from the upstream sig-network repo.
|
||||
# Cilium requires these CRDs to exist before gatewayAPI.enabled=true works.
|
||||
#
|
||||
# Standard channel includes: GatewayClass, Gateway, HTTPRoute, GRPCRoute,
|
||||
# ReferenceGrant
|
||||
# Experimental channel adds: TCPRoute, TLSRoute, UDPRoute (added below)
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: gateway-api-crds
|
||||
namespace: argocd
|
||||
annotations:
|
||||
# Wave 3.5 — after cert-manager (3), before cilium-config (4)
|
||||
# ArgoCD sync waves are integers, so we use 3 here and rely on
|
||||
# sync ordering within the wave. Add a sync-wave of 3 to cert-manager
|
||||
# and this will naturally sequence correctly.
|
||||
argocd.argoproj.io/sync-wave: "3"
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://gitea.mk-labs.cloud/rblundon/homelab.git
|
||||
targetRevision: main
|
||||
path: cluster/platform/gateway-api/manifests
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: gateway-api
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: false # Never prune CRDs — too dangerous
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
- Replace=true # CRDs require Replace not Patch for updates
|
||||
57
cluster/platform/gateway-api/gateway.yaml
Normal file
57
cluster/platform/gateway-api/gateway.yaml
Normal file
@@ -0,0 +1,57 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Gateway — fastpass-gateway
|
||||
# Wave 5.5 — after cilium-config (4) and ingress-nginx (5)
|
||||
#
|
||||
# This is the central Gateway that all HTTPRoute resources attach to.
|
||||
# Cilium operator creates a LoadBalancer service for this Gateway automatically,
|
||||
# which will receive an IP from the cilium-gateway-pool (10.1.71.90-.99).
|
||||
#
|
||||
# Listeners:
|
||||
# - HTTP (port 80) — redirect to HTTPS or direct for internal services
|
||||
# - HTTPS (port 443) — TLS termination via cert-manager certificates
|
||||
# - TCP (port configurable) — for non-HTTP services (future use)
|
||||
#
|
||||
# Usage: Applications create HTTPRoute resources that reference this Gateway:
|
||||
#
|
||||
# spec:
|
||||
# parentRefs:
|
||||
# - name: fastpass-gateway
|
||||
# namespace: gateway-system
|
||||
#
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: fastpass-gateway
|
||||
namespace: gateway-system
|
||||
annotations:
|
||||
# Request a specific IP from the cilium-gateway-pool
|
||||
io.cilium/lb-ipam-ips: "10.1.71.90"
|
||||
spec:
|
||||
gatewayClassName: cilium
|
||||
listeners:
|
||||
# HTTP listener — accepts from all namespaces
|
||||
- name: http
|
||||
protocol: HTTP
|
||||
port: 80
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
|
||||
# HTTPS listener — TLS termination
|
||||
# Each service provides its own cert via cert-manager Certificate resource
|
||||
# and a ReferenceGrant allowing the Gateway to read it cross-namespace
|
||||
- name: https
|
||||
protocol: HTTPS
|
||||
port: 443
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
tls:
|
||||
mode: Terminate
|
||||
certificateRefs:
|
||||
# ArgoCD TLS cert — first service on the Gateway
|
||||
# Add additional certs here as services are added, or use
|
||||
# a wildcard cert (*.local.mk-labs.cloud) once the pattern is proven
|
||||
- name: argocd-tls
|
||||
namespace: argocd
|
||||
40
cluster/platform/gateway-api/manifests/README.md
Normal file
40
cluster/platform/gateway-api/manifests/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Gateway API CRDs — Standard + Experimental Channel
|
||||
# Version: v1.2.1 (latest stable as of May 2026)
|
||||
#
|
||||
# We reference upstream CRD manifests directly. ArgoCD will apply them
|
||||
# from the upstream GitHub URL via the source config in application.yaml.
|
||||
#
|
||||
# To upgrade Gateway API CRDs:
|
||||
# 1. Update the version tag in the URLs below
|
||||
# 2. Commit and push — ArgoCD applies automatically
|
||||
#
|
||||
# Standard channel CRDs (stable, production-ready):
|
||||
# - GatewayClass
|
||||
# - Gateway
|
||||
# - HTTPRoute
|
||||
# - GRPCRoute
|
||||
# - ReferenceGrant
|
||||
#
|
||||
# Experimental channel additions (used for TCPRoute for non-HTTP services):
|
||||
# - TLSRoute
|
||||
# - TCPRoute
|
||||
#
|
||||
# Source: https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.2.1
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# NOTE: ArgoCD applies these from the upstream URL. This file documents
|
||||
# the versions in use. The actual application.yaml points ArgoCD at this
|
||||
# directory, so place the CRD manifests here by running:
|
||||
#
|
||||
# curl -L https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml \
|
||||
# -o cluster/platform/gateway-api/manifests/standard-install.yaml
|
||||
#
|
||||
# curl -L https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/experimental-install.yaml \
|
||||
# -o cluster/platform/gateway-api/manifests/experimental-install.yaml
|
||||
#
|
||||
# Commit both files. ArgoCD will apply them on next sync.
|
||||
#
|
||||
# The commands above should be run from city-hall or your workstation
|
||||
# and the resulting files committed to git. We vendor them rather than
|
||||
# fetching at runtime so the exact CRD versions are pinned in git.
|
||||
7
cluster/platform/gateway-api/namespace.yaml
Normal file
7
cluster/platform/gateway-api/namespace.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: gateway-system
|
||||
labels:
|
||||
# Allow all namespaces to attach Routes to Gateways in this namespace
|
||||
kubernetes.io/metadata.name: gateway-system
|
||||
29
cluster/platform/ingress-nginx/application.yaml
Normal file
29
cluster/platform/ingress-nginx/application.yaml
Normal file
@@ -0,0 +1,29 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Platform: ingress-nginx
|
||||
# Wave 5 — depends on Cilium LB pool (wave 4) to get a LoadBalancer IP
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: ingress-nginx
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "5"
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://kubernetes.github.io/ingress-nginx
|
||||
chart: ingress-nginx
|
||||
targetRevision: 4.12.1
|
||||
helm:
|
||||
valuesFile: values.yaml
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: ingress-nginx
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
43
cluster/platform/ingress-nginx/values.yaml
Normal file
43
cluster/platform/ingress-nginx/values.yaml
Normal file
@@ -0,0 +1,43 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# ingress-nginx — Helm Values
|
||||
# Chart: https://kubernetes.github.io/ingress-nginx
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
controller:
|
||||
# Request a specific IP from the Cilium LB pool
|
||||
# 10.1.71.80 — first address in the pool, reserved for ingress
|
||||
service:
|
||||
loadBalancerIP: 10.1.71.80
|
||||
annotations:
|
||||
# Tell Cilium which pool to use (optional if only one pool exists)
|
||||
io.cilium/lb-ipam-ips: "10.1.71.80"
|
||||
|
||||
# IngressClass name — referenced by all Ingress resources
|
||||
ingressClassResource:
|
||||
name: nginx
|
||||
enabled: true
|
||||
default: true
|
||||
|
||||
# Pass real client IPs through to backends
|
||||
config:
|
||||
use-forwarded-headers: "true"
|
||||
compute-full-forwarded-for: "true"
|
||||
use-proxy-protocol: "false"
|
||||
|
||||
# Prometheus metrics
|
||||
metrics:
|
||||
enabled: true
|
||||
serviceMonitor:
|
||||
enabled: false # enable once Prometheus operator is in-cluster
|
||||
|
||||
# 2 replicas for basic resilience across worker nodes
|
||||
replicaCount: 2
|
||||
|
||||
# Spread across worker nodes
|
||||
topologySpreadConstraints:
|
||||
- maxSkew: 1
|
||||
topologyKey: kubernetes.io/hostname
|
||||
whenUnsatisfiable: DoNotSchedule
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: ingress-nginx
|
||||
41
cluster/platform/onepassword-connect/application.yaml
Normal file
41
cluster/platform/onepassword-connect/application.yaml
Normal file
@@ -0,0 +1,41 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Platform: 1Password Connect Server
|
||||
# Wave 2 — depends on ESO (wave 1) being healthy
|
||||
#
|
||||
# IMPORTANT — Bootstrap secret:
|
||||
# Before this Application can sync successfully, the 1Password credentials
|
||||
# file must be applied manually ONE TIME:
|
||||
#
|
||||
# kubectl create namespace onepassword-connect
|
||||
# kubectl create secret generic op-credentials \
|
||||
# --from-file=1password-credentials.json \
|
||||
# -n onepassword-connect
|
||||
#
|
||||
# This is the ONLY secret that cannot flow through ESO (it IS the ESO backend).
|
||||
# Store the credentials file securely in 1Password itself as a backup.
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: onepassword-connect
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "2"
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: https://1password.github.io/connect-helm-charts
|
||||
chart: connect
|
||||
targetRevision: 1.16.0
|
||||
helm:
|
||||
valuesFile: values.yaml
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: onepassword-connect
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
- ServerSideApply=true
|
||||
@@ -0,0 +1,32 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# ClusterSecretStore — 1Password Connect
|
||||
#
|
||||
# This is the bridge between ESO and 1Password Connect. Once this exists,
|
||||
# any ExternalSecret in any namespace can reference:
|
||||
# secretStoreRef:
|
||||
# kind: ClusterSecretStore
|
||||
# name: onepassword-connect
|
||||
#
|
||||
# The connect-token secret must exist in the onepassword-connect namespace.
|
||||
# Generate a token in 1Password: Developer Tools > Connect Servers > <your server>
|
||||
# Then apply once:
|
||||
# kubectl create secret generic connect-token \
|
||||
# --from-literal=token=<your-token> \
|
||||
# -n onepassword-connect
|
||||
# ------------------------------------------------------------------------------
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ClusterSecretStore
|
||||
metadata:
|
||||
name: onepassword-connect
|
||||
spec:
|
||||
provider:
|
||||
onepassword:
|
||||
connectHost: http://onepassword-connect.onepassword-connect.svc.cluster.local:8080
|
||||
vaults:
|
||||
mk-labs: 1 # vault name in 1Password : priority
|
||||
auth:
|
||||
secretRef:
|
||||
connectTokenSecretRef:
|
||||
name: connect-token
|
||||
key: token
|
||||
namespace: onepassword-connect
|
||||
24
cluster/platform/onepassword-connect/values.yaml
Normal file
24
cluster/platform/onepassword-connect/values.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# 1Password Connect Server — Helm Values
|
||||
# Chart: https://1password.github.io/connect-helm-charts
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
connect:
|
||||
# Reference the manually-bootstrapped credentials secret
|
||||
credentialsName: op-credentials
|
||||
|
||||
# Connect server API token — pulled from the credentials secret
|
||||
# The token is generated in your 1Password account under
|
||||
# Developer Tools > Connect Servers
|
||||
serviceType: ClusterIP
|
||||
|
||||
# Operator manages automatic secret injection (optional, disable if not using)
|
||||
operator:
|
||||
create: false
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ClusterSecretStore — tells ESO to use this Connect server as its backend.
|
||||
# Applied as a raw manifest alongside the Helm chart via ArgoCD's multi-source
|
||||
# (or manually after Connect is healthy — whichever you prefer).
|
||||
# See: cluster-secret-store.yaml
|
||||
# ------------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user