1 Commits

Author SHA1 Message Date
85d150d3d4 rbac 2026-04-27 11:03:12 +02:00
112 changed files with 540 additions and 1219 deletions

View File

@@ -57,7 +57,7 @@ This repository contains the complete GitOps configuration for our Kubernetes cl
### What's Inside
- **Infrastructure Applications**: Traefik, Cert-Manager, Kyverno, Prometheus, Grafana, Loki, Tempo, Sealed Secrets, Homepage (platform dashboard)
- **Infrastructure Applications**: Traefik, Cert-Manager, Kyverno, Prometheus, Grafana, Loki, Tempo, Sealed Secrets
- **Business Applications**: MCP10X, MusicMan, Dot-AI Stack, ArgoCD MCP
- **Policies**: Kyverno security policies for secret management, namespace controls, pod verification
- **Monitoring**: Full observability stack with metrics, logs, traces, and alerting
@@ -84,25 +84,24 @@ This repository contains the complete GitOps configuration for our Kubernetes cl
├── _app-of-apps.yaml # Root ArgoCD Application (App-of-Apps pattern)
├── infra/ # Infrastructure ArgoCD Applications (Kustomize multi-cluster)
│ ├── base/ # Base ArgoCD Application manifests (one dir per component)
│ │ ├── kustomization.yaml # Aggregates all component subdirectories
│ │ ├── traefik-application/
│ │ ├── kustomization.yaml
│ │ │ └── traefik-application.yaml
│ │ ├── keycloak/
│ │ │ ├── kustomization.yaml
│ │ │ └── keycloak.yaml
│ │ ├── grafana/
│ │ ├── prometheus/
│ │ ── ... # Each component in its own subdirectory
│ │ └── secrets/
│ ├── base/ # Base ArgoCD Application manifests (EU defaults)
│ │ ├── kustomization.yaml
│ │ ├── traefik-application.yaml
│ │ ├── keycloak.yaml
│ │ ── grafana.yaml
│ │ ├── gitea.yaml
│ │ ├── gitea-actions.yaml
│ │ ├── tempo.yaml
│ │ ├── renovate.yaml
│ │ ├── ... # All other Application manifests
│ │ ── secrets.yaml
│ ├── overlays/ # Per-cluster overrides (Kustomize)
│ │ ├── upc-dev/ # UpCloud Dev — includes all base components
│ │ ├── upc-prod/ # UpCloud Prod — all components + patches
│ │ ├── aks-dev/ # Azure AKS Dev — selective components only
│ │ ├── aks-prod/ # Azure AKS Prod
│ │ ├── upc-dev/ # UpCloud Dev (uses base as-is)
│ │ ├── upc-prod/ # UpCloud Prod (patches value paths)
│ │ ├── eks-dev/ # AWS EKS Dev
│ │ ├── eks-prod/ # AWS EKS Prod
│ │ ├── aks-dev/ # Azure AKS Dev
│ │ ├── aks-prod/ # Azure AKS Prod
│ │ ├── gke-dev/ # GCP GKE Dev
│ │ └── gke-prod/ # GCP GKE Prod
│ ├── dashboards/ # Grafana dashboard ConfigMaps
@@ -117,18 +116,11 @@ This repository contains the complete GitOps configuration for our Kubernetes cl
│ ├── gke-dev/ # GCP GKE Dev
│ └── gke-prod/ # GCP GKE Prod
├── apps/ # Business Applications (Kustomize, same pattern as infra)
│ ├── base/ # One subdirectory per app
│ ├── kustomization.yaml
│ ├── musicman/
│ ├── mcp10x/
│ │ ├── dot-ai-stack/
│ │ ├── ts-mcp/
│ │ └── argo-mcp/
│ └── overlays/ # Per-cluster: cherry-pick or include all
│ ├── upc-dev/ # All apps
│ ├── upc-prod/ # All apps + patches
│ └── aks-dev/ # Selective apps only
├── apps/ # Business Applications
│ ├── mcp10x.yaml
│ ├── musicman.yaml
│ ├── dot-ai-stack.yaml
└── argo-mcp.yaml
├── cluster-resources/ # Cluster-wide Kubernetes resources
│ ├── letsencrypt-issuer.yaml
@@ -380,7 +372,7 @@ kubectl patch application myapp -n argocd \
## 📖 Key Concepts
### App-of-Apps Pattern
`_app-of-apps-{cluster}.yaml` is the root Application that manages all other Applications in `infra/`. Each component in `infra/base/` lives in its own subdirectory (e.g., `infra/base/grafana/`). Overlays can either include **all** components (via `../../base`) or **cherry-pick** specific ones (via `../../base/grafana`, `../../base/prometheus`, etc.). Per-cluster patches swap Helm value file paths. Supported clusters: `upc-dev`, `upc-prod`, `eks-dev`, `eks-prod`, `aks-dev`, `aks-prod`, `gke-dev`, `gke-prod`.
`_app-of-apps-{cluster}.yaml` is the root Application that manages all other Applications in `infra/`. Kustomize overlays in `infra/overlays/{cluster}/` render the base Applications with per-cluster patches (e.g., swapping value file paths). Supported clusters: `upc-dev`, `upc-prod`, `eks-dev`, `eks-prod`, `aks-dev`, `aks-prod`, `gke-dev`, `gke-prod`.
### Multi-Source Pattern
Applications reference both:

View File

@@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- argo-mcp.yaml
- argocdmcp-auth-oidc-sealed.yaml
- argocd-mcp-credentials.yaml

View File

@@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- dot-ai-stack.yaml
- dot-ai-secrets.yaml

View File

@@ -1,8 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- dot-ai-stack
- mcp10x
- musicman
- ts-mcp
- argo-mcp
- dot-ai-stack.yaml
- mcp10x.yaml
- musicman.yaml
- ts-mcp.yaml
- argo-mcp.yaml

View File

@@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- mcp10x.yaml
- forte10x-app-credentials-sealed.yaml

View File

@@ -36,8 +36,13 @@ spec:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- Validate=true
- ServerSideApply=false
- Replace=false
retry:
limit: 5
backoff:

View File

@@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- musicman.yaml
- musicman-credentials.yaml

View File

@@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ts-mcp.yaml
- ts-mcp-secrets-sealed.yaml

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/musicman

View File

@@ -1,47 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: dbunk-demo
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "12"
labels:
app.kubernetes.io/name: dbunk-demo
app.kubernetes.io/part-of: apps
app.kubernetes.io/managed-by: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
sources:
- repoURL: ssh://git@git.forteapps.net:2222/Forte/forte-helm.git
path: forteapp
targetRevision: HEAD
helm:
valueFiles:
- $values/dbunk-demo/values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: dbunk-demo
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- Validate=true
- ServerSideApply=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- dbunk-demo.yaml

View File

@@ -1,53 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: feedback
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "12"
labels:
app.kubernetes.io/name: feedback
app.kubernetes.io/part-of: apps
app.kubernetes.io/managed-by: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
sources:
- repoURL: ssh://git@git.forteapps.net:2222/Forte/forte-helm.git
path: forteapp
targetRevision: HEAD
helm:
valueFiles:
- $values/feedback/values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/helm-prod-values.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: feedback
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- Validate=true
- ServerSideApply=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
ignoreDifferences:
- group: apps
kind: StatefulSet
jsonPointers:
- /spec/volumeClaimTemplates

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- feedback.yaml

View File

@@ -2,8 +2,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- dbunk-demo
- feedback
# No patches needed — base already has "upc-dev" paths
# upc-dev is the default/base cluster

View File

@@ -18,7 +18,7 @@ echo "Bootstrapping cluster: ${clusterName} (${CLUSTER})..."
Bootstrap()
{
ArgoCd
Gitea
# Gitea
}

View File

@@ -245,12 +245,6 @@ spec:
secretKeyRef:
name: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-credentials-secret\" || 'auth-oidc' }}"
key: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-credentials-secret-key\" || 'client-secret' }}"
- name: AUTH_OIDC_IDP_HINT
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-idp-hint\" || '' }}"
- name: AUTH_OIDC_BROKER_ALIAS
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-broker-alias\" || '' }}"
- name: AUTH_OIDC_BROKER_TOKEN_HEADER
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-oidc-broker-token-header\" || '' }}"
resources:
limits:
cpu: 50m
@@ -330,8 +324,6 @@ spec:
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-public-paths\" || '/healthz' }}"
- name: AUTH_MCP_SCOPES_SUPPORTED
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-mcp-scopes\" || 'profile' }}"
- name: AUTH_MCP_IDP_HINT
value: "{{ request.object.metadata.annotations.\"policies.forteapps.io/auth-mcp-idp-hint\" || '' }}"
resources:
limits:
cpu: 50m

View File

@@ -1,12 +1,12 @@
# Cluster config reference — values must match the corresponding overlay files.
# Read by bootstrap.sh at install time; NOT auto-propagated to ArgoCD value files.
clusterName: k8s-launchpad # → infra/values/aks-dev/argocd-values.yaml (notifications.context.clusterName)
domain: example.com # → infra/values/base/gitea-values.yaml, renovate-values.yaml, keycloak-values.yaml (subdomains)
argocdDomain: argocd.example.com # → infra/values/aks-dev/argocd-values.yaml (global.domain)
grafanaDomain: grafana.example.com # → infra/values/aks-dev/grafana-values.yaml (ingress.hosts)
keycloakDomain: id.example.com # → infra/values/aks-dev/keycloak-values.yaml (ingress.hostname)
dotaiDomain: kubemcp.example.com # → infra/values/aks-dev/dot-ai-stack-values.yaml (dot-ai.ingress.host) — create if needed
dotaiUiDomain: kubemcpui.example.com # → infra/values/aks-dev/dot-ai-stack-values.yaml (dot-ai-ui.ingress.host) — create if needed
letsencryptEmail: admin@example.com # → cluster-resources/letsencrypt-issuer.yaml (spec.acme.email)
clusterName: dev-aks # → infra/values/aks-dev/argocd-values.yaml (notifications.context.clusterName)
domain: example.com # → infra/values/base/gitea-values.yaml, renovate-values.yaml, keycloak-values.yaml (subdomains)
argocdDomain: argocd.example.com # → infra/values/aks-dev/argocd-values.yaml (global.domain)
grafanaDomain: grafana.example.com # → infra/values/aks-dev/grafana-values.yaml (ingress.hosts)
keycloakDomain: id.example.com # → infra/values/aks-dev/keycloak-values.yaml (ingress.hostname)
dotaiDomain: kubemcp.example.com # → infra/values/aks-dev/dot-ai-stack-values.yaml (dot-ai.ingress.host) — create if needed
dotaiUiDomain: kubemcpui.example.com # → infra/values/aks-dev/dot-ai-stack-values.yaml (dot-ai-ui.ingress.host) — create if needed
letsencryptEmail: admin@example.com # → cluster-resources/letsencrypt-issuer.yaml (spec.acme.email)
trustedIPs: "10.0.0.0/8,168.63.129.16/32" # → infra/values/aks-dev/traefik-values.yaml (ports.*.trustedIPs) — VNet CIDR + Azure health probe
cloudProvider: azure # → determines overlay directory and cloud-specific LB/storage annotations
cloudProvider: azure # → determines overlay directory and cloud-specific LB/storage annotations

View File

@@ -120,25 +120,24 @@ launchpad/
├── _app-of-apps-upc-prod.yaml # Root ArgoCD Application (upc-prod cluster)
├── infra/ # Infrastructure ArgoCD Applications (Kustomize)
│ ├── base/ # Base Application manifests (one dir per component)
│ │ ├── kustomization.yaml # Aggregates all component subdirectories
│ │ ├── traefik-application/
│ │ ├── kustomization.yaml
│ │ │ └── traefik-application.yaml
│ │ ├── keycloak/
│ │ │ ├── kustomization.yaml
│ │ │ └── keycloak.yaml
│ │ ├── grafana/
│ │ ├── prometheus/
│ │ ── ... # Each component in its own subdirectory
│ │ └── secrets/
│ ├── base/ # Base Application manifests (upc-dev defaults)
│ │ ├── kustomization.yaml
│ │ ├── traefik-application.yaml
│ │ ├── keycloak.yaml
│ │ ── grafana.yaml
│ │ ├── gitea.yaml
│ │ ├── gitea-actions.yaml
│ │ ├── tempo.yaml
│ │ ├── renovate.yaml
│ │ ├── ... # All other Application manifests
│ │ ── secrets.yaml
│ ├── overlays/ # Per-cluster Kustomize overrides
│ │ ├── upc-dev/ # UpCloud Dev — includes all (resources: ../../base)
│ │ ├── upc-prod/ # UpCloud Prod — all + patches
│ │ ├── aks-dev/ # Azure AKS Dev — selective components
│ │ ├── aks-prod/ # Azure AKS Prod
│ │ ├── upc-dev/ # UpCloud Dev (uses base as-is)
│ │ ├── upc-prod/ # UpCloud Prod (patches value paths)
│ │ ├── eks-dev/ # AWS EKS Dev
│ │ ├── eks-prod/ # AWS EKS Prod
│ │ ├── aks-dev/ # Azure AKS Dev
│ │ ├── aks-prod/ # Azure AKS Prod
│ │ ├── gke-dev/ # GCP GKE Dev
│ │ └── gke-prod/ # GCP GKE Prod
│ ├── dashboards/ # Grafana dashboard ConfigMaps
@@ -150,17 +149,13 @@ launchpad/
│ └── gcp-{dev,prod}/ # GCP: premium-rwo, L4 LB
├── apps/ # Business Application ArgoCD manifests (Kustomize)
│ ├── base/ # One subdirectory per app
│ ├── base/ # Base app manifests
│ │ ├── kustomization.yaml
│ │ ├── musicman/
│ │ ── mcp10x/
│ │ ├── dot-ai-stack/
│ │ ├── ts-mcp/
│ │ └── argo-mcp/
│ │ ├── dot-ai-stack.yaml
│ │ ── ...
│ └── overlays/
│ ├── upc-dev/ # All apps (resources: ../../base)
── upc-prod/ # All apps + patches
│ └── aks-dev/ # Selective apps only
│ ├── upc-dev/ # Uses base as-is
── upc-prod/ # Patches value paths
├── cluster-resources/ # Cluster-wide Kubernetes resources
│ ├── ...
@@ -176,8 +171,6 @@ launchpad/
**Key Points**:
- `_app-of-apps-upc-dev.yaml` and `_app-of-apps-upc-prod.yaml` are the per-cluster root Applications
- Each component in `base/` has its own subdirectory with a `kustomization.yaml`
- Overlays can include **all** components (`resources: [../../base]`) or **cherry-pick** specific ones (`resources: [../../base/grafana, ../../base/prometheus]`)
- Kustomize overlays in `infra/overlays/` render base Applications with per-cluster patches
- Helm values are split: `values/base/` (shared) + `values/upc-dev/` or `values/upc-prod/` (cluster-specific)
- `apps/` follows the same base/overlays pattern for business applications
@@ -360,30 +353,16 @@ spec:
### Multi-Cluster Pattern
Kustomize overlays enable deploying the same Applications across clusters with different configurations.
Each component in `infra/base/` and `apps/base/` lives in its own subdirectory. Overlays define **which components to include** and optionally **patch** them:
Kustomize overlays enable deploying the same Applications across clusters with different configurations:
```yaml
# Option 1: Include ALL components (full cluster)
# infra/overlays/upc-dev/kustomization.yaml
resources:
- ../../base # Pulls in every component subdirectory
# infra/base/ contains default (upc-dev) Applications
# Helm values are layered: base + cluster-specific
valueFiles:
- $values/infra/values/base/traefik-values.yaml # Shared config
- $values/infra/values/upc-dev/traefik-values.yaml # Cluster-specific
# Option 2: Cherry-pick specific components (lightweight cluster)
# infra/overlays/aks-dev/kustomization.yaml
resources:
- ../../base/traefik-application
- ../../base/grafana
- ../../base/prometheus
- ../../base/loki
# Only listed components are deployed — others are excluded
```
Per-cluster patches swap Helm value file paths:
```yaml
# infra/overlays/upc-prod/kustomization.yaml
# infra/overlays/upc-prod/kustomization.yaml patches the second valueFile
patches:
- target:
kind: Application

View File

@@ -76,28 +76,33 @@ launchpad/
├── _app-of-apps-upc-dev.yaml # Root ArgoCD Application (upc-dev)
├── _app-of-apps-upc-prod.yaml # Root ArgoCD Application (upc-prod)
├── infra/ # Infrastructure applications (Kustomize)
│ ├── base/ # One subdirectory per component
│ ├── kustomization.yaml # Aggregates all component subdirectories
│ ├── traefik-application/
│ │ ├── kustomization.yaml
│ │ └── traefik-application.yaml
│ ├── keycloak/
│ │ ├── kustomization.yaml
│ │ └── keycloak.yaml
│ ├── grafana/
│ ├── prometheus/
│ ├── loki/
│ ├── tempo/
│ ├── gitea/
│ ├── opencost/
│ ├── ... # Each component in own directory
│ └── secrets/
│ ├── overlays/ # Per-cluster: include all or cherry-pick
│ │ ├── upc-dev/ # resources: [../../base] (all components)
│ │ ├── upc-prod/ # resources: [../../base] + patches
│ │ ├── aks-dev/ # resources: [../../base/grafana, ...] (selective)
│ │ ── .../ # 8 clusters total
├── infra/ # Infrastructure applications
│ ├── cluster-resources-application.yaml
│ ├── enterprise-apps.yaml
│ ├── traefik-application.yaml
├── cert-manager-application.yaml
├── kyverno.yaml
│ ├── kyverno-policies.yaml
├── prometheus.yaml
├── grafana.yaml
│ ├── loki.yaml
│ ├── tempo.yaml
│ ├── fluent-bit.yaml
│ ├── gitea.yaml
│ ├── gitea-actions.yaml
│ ├── sealedsecrets.yaml
│ ├── secrets.yaml
├── renovate.yaml
│ ├── base/ # ArgoCD Application manifests (Kustomize base)
│ │ ├── gitea.yaml
│ │ ├── opencost.yaml
│ │ ├── traefik-application.yaml
│ │ ── keycloak.yaml
│ │ ├── grafana.yaml
│ │ └── ...
│ ├── overlays/
│ │ └── upc-prod/
│ │ └── kustomization.yaml # Patches upc-dev → upc-prod valueFile paths
│ └── values/
│ ├── base/ # Cloud-agnostic Helm values
│ │ ├── gitea-values.yaml
@@ -117,18 +122,11 @@ launchpad/
│ ├── gitea-values.yaml
│ └── opencost-values.yaml
├── apps/ # Business applications (Kustomize)
│ ├── base/ # One subdirectory per app
│ ├── kustomization.yaml
│ ├── musicman/
│ ├── mcp10x/
│ │ ├── dot-ai-stack/
│ │ ├── ts-mcp/
│ │ └── argo-mcp/
│ └── overlays/ # Per-cluster: include all or cherry-pick
│ ├── upc-dev/
│ ├── upc-prod/
│ └── aks-dev/ # Selective apps only
├── apps/ # Business applications
│ ├── mcp10x.yaml
│ ├── musicman.yaml
│ ├── dot-ai-stack.yaml
└── argo-mcp.yaml
├── cluster-resources/ # Cluster-level resources
│ ├── cert-manager-namespace.yaml
@@ -695,7 +693,25 @@ configs:
scopes: '[groups]'
```
**Access Control**: Only users in Keycloak groups `ArgoCD Admins` or `ArgoCD Viewers` can access ArgoCD. Users not in either group are denied (empty `policy.default`). Assign users to groups in Keycloak admin console.
**Access Control**: Only users in declared Keycloak groups can access ArgoCD. Users not in any group are denied (`policy.default: ""`). Assign users to groups in Keycloak admin console.
| KC Group | ArgoCD Role | Access |
|----------|-------------|--------|
| `ArgoCD Admins` | `role:admin` | Full control over all apps |
| `ArgoCD Viewers` | `role:readonly` | Read-only access to all apps |
| `Observability Team` | `role:observability` | Get/sync monitoring apps (prometheus, loki, fluent-bit, tempo, grafana, opencost) |
| `Dev Tools Team` | `role:devtools` | Get/sync dev tool apps (gitea, gitea-actions, renovate, karpor) |
| `App Developers` | `role:app-dev` | Get/sync/action on enterprise-apps only |
**Per-Cluster RBAC**: Add cluster-specific policies in `infra/values/<cluster>/argocd-values.yaml` using `configs.rbac.policy.<cluster>.csv`. ArgoCD concatenates all `policy.*.csv` keys alphabetically after `policy.csv`. Example:
```yaml
# infra/values/upc-dev/argocd-values.yaml
configs:
rbac:
policy.upc-dev.csv: |
p, role:staging-deployer, applications, sync, default/enterprise-apps, allow
g, Staging Deployers, role:staging-deployer
```
- ArgoCD does NOT add `openid` implicitly — must include in `requestedScopes`
- Do NOT add `groups` as a scope — the KC groups mapper emits the claim regardless
@@ -725,59 +741,6 @@ TLS terminates at Traefik; ArgoCD runs in insecure mode behind the proxy.
## Infrastructure Components
### Homepage (Platform Dashboard)
**Chart**: `jameswynn/homepage`
**Namespace**: `homepage`
**URL**: `https://start.forteapps.net`
Platform dashboard that auto-discovers deployed apps via Kubernetes service annotations.
**Discovery mechanism**: Services annotated with `gethomepage.dev/enabled: "true"` appear in the dashboard. Apps not deployed = annotations absent = not shown. Fully dynamic per environment.
**Annotated services**:
| Service | Namespace | Group | Widget |
|---------|-----------|-------|--------|
| `gitea-http` | `gitea` | DevOps | `gitea` |
| `argocd-server` | `argocd` | DevOps | `argocd` |
| `keycloak` | `keycloak` | Identity | none |
| `grafana` | `monitoring` | Monitoring | `grafana` |
| `karpor-server` | `karpor` | DevOps | none |
**Adding a new app**: Annotate the app's Service in its Helm values:
```yaml
service:
annotations:
gethomepage.dev/enabled: "true"
gethomepage.dev/name: "My App"
gethomepage.dev/description: "What it does"
gethomepage.dev/group: "GroupName"
gethomepage.dev/icon: "icon-name" # https://github.com/walkxcode/dashboard-icons
gethomepage.dev/href: "https://myapp.forteapps.net"
# Optional live widget:
gethomepage.dev/widget.type: "myapp"
gethomepage.dev/widget.url: "https://myapp.forteapps.net"
# gethomepage.dev/widget.key: "{{HOMEPAGE_VAR_MYAPP_TOKEN}}"
```
**Widget API credentials**: Inject via env vars into the Homepage pod:
```yaml
# In homepage-values.yaml per environment
env:
- name: HOMEPAGE_VAR_GRAFANA_TOKEN
valueFrom:
secretKeyRef:
name: homepage-widget-credentials
key: grafana-token
```
Then reference as `gethomepage.dev/widget.key: "{{HOMEPAGE_VAR_GRAFANA_TOKEN}}"`.
**Values files**:
- `infra/values/base/homepage-values.yaml` — RBAC, kubernetes mode, layout
- `infra/values/{env}/homepage-values.yaml` — hostname per environment
---
### Traefik
**Chart**: `traefik/traefik`
@@ -1384,46 +1347,6 @@ spec:
- Adds source tracking annotations (`keycloak.forteapps.net/source-namespace`, `keycloak.forteapps.net/source-name`)
- `synchronize: true` — changes to the source Secret are reflected in the clone
### Keycloak Microsoft/Entra Identity Provider
**File**: `infra/values/upc-dev/keycloak-values.yaml`
**Namespace**: `keycloak`
**Purpose**: Configures Microsoft Entra (Azure AD) as an external identity provider for the Forte realm, enabling SSO via Microsoft accounts with token storage for downstream API access (e.g., Microsoft Graph).
**Configuration via keycloakConfigCli**:
- IdP alias: `forte-entra`, provider: `microsoft`
- Client secret injected from `microsoft-idp-credentials` Secret via `$(env:MS_IDP_CLIENT_SECRET)` syntax
- `extraEnvVarsSecret: microsoft-idp-credentials` makes the Secret available as env vars to config-cli
**Key Configuration Notes**:
| Field | Location | Notes |
|-------|----------|-------|
| `tenant` | `config.tenant` | **Must be `tenant`, NOT `tenantId`** — wrong key silently falls back to `common` (multi-tenant) |
| `storeToken` | Top-level IdP field | **NOT inside `config`** — enables broker token storage for KC broker API |
| `defaultScope` | `config.defaultScope` | Space-separated: `openid email profile User.Read Mail.Send` |
| `syncMode` | `config.syncMode` | `IMPORT` — imports user on first login |
**Token Storage & Broker Access**:
- `storeToken: true` persists the Entra access token in Keycloak
- Realm role `default-roles-forte` includes composite `broker.read-token` — grants all realm users access to broker token API
- Broker token retrievable via: `GET /realms/forte/broker/forte-entra/token`
**Identity Provider Mappers**:
- `forte-entra-email`: Hardcodes `emailVerified=true` for Entra-authenticated users (Entra guarantees email verification)
**Required Secret** (`microsoft-idp-credentials`):
```yaml
apiVersion: v1
kind: Secret
metadata:
name: microsoft-idp-credentials
namespace: keycloak
stringData:
MS_IDP_CLIENT_SECRET: "<entra-app-client-secret>"
```
### Default Namespace Blocker
**File**: `cluster-resources/policies/default-ns-blocker.yaml`

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- cert-manager-application.yaml

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- cluster-resources-application.yaml

View File

@@ -1,42 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: databunker
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "1"
labels:
app.kubernetes.io/name: databunker
app.kubernetes.io/part-of: identity
app.kubernetes.io/managed-by: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
sources:
- repoURL: https://securitybunker.github.io/databunkerpro-setup
chart: databunkerpro
targetRevision: "0.1.0"
helm:
releaseName: databunkerpro
valueFiles:
- $values/infra/values/base/databunker-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: databunker
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- Validate=true
- ServerSideApply=true

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- databunker.yaml

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- enterprise-apps.yaml

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- fluent-bit.yaml

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gitea-actions.yaml

View File

@@ -1,8 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gitea.yaml
- gitea-backup-s3-sealed.yaml
- gitea-credentials-sealed.yaml
- gitea-runner-token-sealed.yaml
- gitea-smtp-secret-sealed.yaml

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- grafana-dashboards.yaml

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- grafana.yaml

View File

@@ -1,21 +0,0 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: homepage-services-reader
rules:
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: homepage-services-reader
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: homepage-services-reader
subjects:
- kind: ServiceAccount
name: homepage
namespace: homepage

View File

@@ -1,16 +0,0 @@
---
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
creationTimestamp: null
name: homepage-widget-credentials
namespace: homepage
spec:
encryptedData:
HOMEPAGE_VAR_GITEA_TOKEN: AgAVN1C931EQpn+sodr3CpjlhORfJVTW8aUr+pGZQb+65Pb8QLGeVGVa7Jv60gDJUX3r+93/jMrEbCOeDL6I4qCz/V35wMCxFZLnXIdkmto0W4MKt6cK8To1/OP7EhQJOGBlSuOFsrwoy+HDtvLIqmyF0nrxhTusm9/NHrw+gCVwSTPhiAX1MCuSOSRWpbXvyNphW8j7aqUaV6ixDt424Fe4alEIShYELcS3EX/VPgsf2p2bhvBRCQOh3LEprkuxSFMuPfCBk06TPTbIN4saNVm0Ke0zW/pxkVNSiIxEnKjOmpPJtacsfWN7du+nQbx276G2qvWrf+iawJVq0Z/SLikA/NUFBL6EjSRfgE3cSOri8sbxsd0AycsFGyp98EM29wE+WOQl52M/lwl02EmCivqkICSO7Jp9pM1ScbmRMa5vcnupsGbVDxhRKLqxhAskt/BXDkRzvHN31gH3YmelES3JuqNMHV0urFxmX2oOX9Pxbtv63csc+zhy1Ui5aoex7TPnLdk7kYLSAE2MSrzT6wHvVhBC5kNnDYVrLehvJrT+eNh0MOLx2wkuJmIOxRAGUyNi5DfDnP6qnvj2aefEymLuOXAIUXH8DbeBtrjsd74HX2hhIfBlPkXvhJR3ks7i5RXjK2/YYHkgJ+nJoW80S9N7ciaRy103g74TNJZt6QzzL5Vb80qZ6yQOD4G081KmTLDmhHjJVIIv9M3nLh2s0IeBV3/Z5qHZmtjN7sSaKAn4MIr5FaH9quhx
HOMEPAGE_VAR_GRAFANA_TOKEN: AgBloBlOlP+R/4VizE1CGpj0wyiwU14BemAnuUpld7OvOGc67dwfDPyponkQXjAZg3UU2cZ70A51WUAuVlAr+25Ktlf/FW2OBqj+1BJOCqMMyu+kv026yjX2aB8dKGzlTxgF8aji+j1mC8vP3vvmgI4Zf2HQAH7uFwLfeo8+QnV5EyhcExSS0xDne+VtOP9jNXbPRayry0DdyRVtaeKAiZacO+45oAJWszWOwmoMTg9FZQkLjER6Q0tyI6NnoNObsFCnh56chZTdzBOYtmPnwld1bP2FjoJDqn8AfRwbPTIj7t0eFP7WLUO7GQKpxVl+pFwJLb5xCOw2+HNtp1BhNCu7icuc0P88IlvwzkbN0lXJbYigVOzyjEo8f/al1DXPM4WaB/Nqmr7Mtt8KTRh2WMVTgiX5jsu25D0rGDvY9gqfBBqswkRhCLsG0v0EN32zXj1/52KYdmB7pk/+2lMwSaGMS11MOenHeU1Z95fGxm9f3EGF0E8xlFr4FowgsNwr+tJQqpM0bT/4mZnaQbGWtKPFizMtsfQFm+rHFcNCrGaOuecslmiIJs8lTm18KlrncsGfxNS64tVXk+LvydU0rwybvpg2rQjEWtAl1IQsaaiz96OAlYxxK1MGxN7KE6F8R4kfnWTZ5Fs1KMmd/DOIVBXyCbqXxk8pbekmaIeNSfv92JNZ0QNJWsBa2vgQ24WI2pb4XiR0BvtLpt3BVlZUcSK92SzUblWmYWVMwHYCJkEeEUV1PhYEmyiN+V/Kq5Qb
template:
metadata:
creationTimestamp: null
name: homepage-widget-credentials
namespace: homepage

View File

@@ -1,43 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: homepage
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "3"
labels:
app.kubernetes.io/name: homepage
app.kubernetes.io/part-of: platform
app.kubernetes.io/managed-by: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
sources:
- repoURL: https://jameswynn.github.io/helm-charts
chart: homepage
targetRevision: "2.1.0"
helm:
releaseName: homepage
valueFiles:
- $values/infra/values/base/homepage-values.yaml
- $values/infra/values/upc-dev/homepage-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: homepage
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- Validate=true
- ServerSideApply=true

View File

@@ -1,6 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- homepage.yaml
- homepage-widget-credentials-sealed.yaml
- homepage-extra-rbac.yaml

View File

@@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- keycloak.yaml
- keycloak-credentials-sealed.yaml

View File

@@ -1,25 +1,23 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- traefik-application
- keycloak
- grafana
- cert-manager-application
- kyverno
- sealedsecrets
- prometheus
- loki
- fluent-bit
- enterprise-apps
- cluster-resources-application
- kyverno-policies
- gitea
- gitea-actions
- opencost
- renovate
- tempo
- grafana-dashboards
- karpor
- databunker
- homepage
- vault
- traefik-application.yaml
- keycloak.yaml
- grafana.yaml
- cert-manager-application.yaml
- kyverno.yaml
- sealedsecrets.yaml
- prometheus.yaml
- loki.yaml
- fluent-bit.yaml
- enterprise-apps.yaml
- cluster-resources-application.yaml
- kyverno-policies.yaml
- secrets.yaml
- gitea.yaml
- gitea-actions.yaml
- opencost.yaml
- renovate.yaml
- tempo.yaml
- grafana-dashboards.yaml
- karpor.yaml

View File

@@ -27,6 +27,7 @@ spec:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- Validate=true

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- kyverno-policies.yaml

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- kyverno.yaml

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- opencost.yaml

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- prometheus.yaml

View File

@@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- renovate.yaml
- renovate-env-sealed.yaml

View File

@@ -1,5 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- sealedsecrets.yaml
- argocd-forte-helm-secret-sealed.yaml

30
infra/base/secrets.yaml Normal file
View File

@@ -0,0 +1,30 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: secrets
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "2"
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: ""
notifications.argoproj.io/subscribe.on-sync-failed.slack: ""
notifications.argoproj.io/subscribe.on-degraded.slack: ""
labels:
app.kubernetes.io/name: secrets
app.kubernetes.io/part-of: platform
app.kubernetes.io/managed-by: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
path: secrets/overlays/upc-dev
destination:
server: https://kubernetes.default.svc
namespace: secrets
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

View File

@@ -1,4 +0,0 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- traefik-application.yaml

View File

@@ -1,49 +0,0 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: vault
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "1"
labels:
app.kubernetes.io/name: vault
app.kubernetes.io/part-of: security
app.kubernetes.io/managed-by: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
sources:
- repoURL: https://helm.releases.hashicorp.com
chart: vault
targetRevision: "0.32.0"
helm:
releaseName: vault
valueFiles:
- $values/infra/values/base/vault-values.yaml
- $values/infra/values/upc-dev/vault-values.yaml
- repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git
targetRevision: HEAD
ref: values
destination:
server: https://kubernetes.default.svc
namespace: vault
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- Validate=true
- ServerSideApply=true
ignoreDifferences:
- group: apps
kind: StatefulSet
jsonPointers:
- /spec/volumeClaimTemplates

View File

@@ -1,31 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/cert-manager-application
- ../../base/cluster-resources-application
- ../../base/grafana
- ../../base/grafana-dashboards
- ../../base/kyverno
- ../../base/kyverno-policies
- ../../base/loki
- ../../base/enterprise-apps
- ../../base/opencost
- ../../base/prometheus
- ../../base/sealedsecrets
- ../../base/tempo
- ../../base/homepage
- ../../base/traefik-application
- ../../base
patches:
# Homepage: swap upc-dev → aks-dev
- target:
kind: Application
name: homepage
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/aks-dev/homepage-values.yaml
# Traefik: swap upc-dev → aks-dev
- target:
kind: Application
@@ -35,6 +13,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/aks-dev/traefik-values.yaml
# Keycloak: swap upc-dev → aks-dev
- target:
kind: Application
name: keycloak
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/aks-dev/keycloak-values.yaml
# Grafana: swap upc-dev → aks-dev
- target:
kind: Application
@@ -44,6 +31,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/aks-dev/grafana-values.yaml
# Gitea: swap upc-dev → aks-dev
- target:
kind: Application
name: gitea
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/aks-dev/gitea-values.yaml
# OpenCost: swap upc-dev → aks-dev
- target:
kind: Application
@@ -53,7 +49,16 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/aks-dev/opencost-values.yaml
# Ent apps: swap upc-dev → aks-prod
# Secrets: change path to aks-dev
- target:
kind: Application
name: secrets
patch: |
- op: replace
path: /spec/source/path
value: secrets/aks-dev
# Enterprise-apps: point to aks-dev overlay
- target:
kind: Application
name: enterprise-apps

View File

@@ -1,18 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/cert-manager-application
- ../../base/cluster-resources-application
- ../../base/grafana
- ../../base/grafana-dashboards
- ../../base/kyverno
- ../../base/kyverno-policies
- ../../base/loki
- ../../base/opencost
- ../../base/prometheus
- ../../base/sealedsecrets
- ../../base/tempo
- ../../base/traefik-application
- ../../base
patches:
# Traefik: swap upc-dev → aks-prod
@@ -24,6 +13,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/aks-prod/traefik-values.yaml
# Keycloak: swap upc-dev → aks-prod
- target:
kind: Application
name: keycloak
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/aks-prod/keycloak-values.yaml
# Grafana: swap upc-dev → aks-prod
- target:
kind: Application
@@ -33,6 +31,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/aks-prod/grafana-values.yaml
# Gitea: swap upc-dev → aks-prod
- target:
kind: Application
name: gitea
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/aks-prod/gitea-values.yaml
# OpenCost: swap upc-dev → aks-prod
- target:
kind: Application
@@ -41,3 +48,21 @@ patches:
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/aks-prod/opencost-values.yaml
# Secrets: change path to aks-prod
- target:
kind: Application
name: secrets
patch: |
- op: replace
path: /spec/source/path
value: secrets/aks-prod
# Enterprise-apps: point to aks-prod overlay
- target:
kind: Application
name: enterprise-apps
patch: |
- op: replace
path: /spec/source/path
value: apps/overlays/aks-prod

View File

@@ -1,18 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/cert-manager-application
- ../../base/cluster-resources-application
- ../../base/grafana
- ../../base/grafana-dashboards
- ../../base/kyverno
- ../../base/kyverno-policies
- ../../base/loki
- ../../base/opencost
- ../../base/prometheus
- ../../base/sealedsecrets
- ../../base/tempo
- ../../base/traefik-application
- ../../base
patches:
# Traefik: swap upc-dev → eks-dev
@@ -24,6 +13,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/eks-dev/traefik-values.yaml
# Keycloak: swap upc-dev → eks-dev
- target:
kind: Application
name: keycloak
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/eks-dev/keycloak-values.yaml
# Grafana: swap upc-dev → eks-dev
- target:
kind: Application
@@ -33,6 +31,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/eks-dev/grafana-values.yaml
# Gitea: swap upc-dev → eks-dev
- target:
kind: Application
name: gitea
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/eks-dev/gitea-values.yaml
# OpenCost: swap upc-dev → eks-dev
- target:
kind: Application
@@ -41,3 +48,21 @@ patches:
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/eks-dev/opencost-values.yaml
# Secrets: change path to eks-dev
- target:
kind: Application
name: secrets
patch: |
- op: replace
path: /spec/source/path
value: secrets/eks-dev
# Enterprise-apps: point to eks-dev overlay
- target:
kind: Application
name: enterprise-apps
patch: |
- op: replace
path: /spec/source/path
value: apps/overlays/eks-dev

View File

@@ -1,18 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/cert-manager-application
- ../../base/cluster-resources-application
- ../../base/grafana
- ../../base/grafana-dashboards
- ../../base/kyverno
- ../../base/kyverno-policies
- ../../base/loki
- ../../base/opencost
- ../../base/prometheus
- ../../base/sealedsecrets
- ../../base/tempo
- ../../base/traefik-application
- ../../base
patches:
# Traefik: swap upc-dev → eks-prod
@@ -24,6 +13,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/eks-prod/traefik-values.yaml
# Keycloak: swap upc-dev → eks-prod
- target:
kind: Application
name: keycloak
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/eks-prod/keycloak-values.yaml
# Grafana: swap upc-dev → eks-prod
- target:
kind: Application
@@ -33,6 +31,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/eks-prod/grafana-values.yaml
# Gitea: swap upc-dev → eks-prod
- target:
kind: Application
name: gitea
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/eks-prod/gitea-values.yaml
# OpenCost: swap upc-dev → eks-prod
- target:
kind: Application
@@ -41,3 +48,21 @@ patches:
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/eks-prod/opencost-values.yaml
# Secrets: change path to eks-prod
- target:
kind: Application
name: secrets
patch: |
- op: replace
path: /spec/source/path
value: secrets/eks-prod
# Enterprise-apps: point to eks-prod overlay
- target:
kind: Application
name: enterprise-apps
patch: |
- op: replace
path: /spec/source/path
value: apps/overlays/eks-prod

View File

@@ -1,18 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/cert-manager-application
- ../../base/cluster-resources-application
- ../../base/grafana
- ../../base/grafana-dashboards
- ../../base/kyverno
- ../../base/kyverno-policies
- ../../base/loki
- ../../base/opencost
- ../../base/prometheus
- ../../base/sealedsecrets
- ../../base/tempo
- ../../base/traefik-application
- ../../base
patches:
# Traefik: swap upc-dev → gke-dev
@@ -24,6 +13,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/gke-dev/traefik-values.yaml
# Keycloak: swap upc-dev → gke-dev
- target:
kind: Application
name: keycloak
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/gke-dev/keycloak-values.yaml
# Grafana: swap upc-dev → gke-dev
- target:
kind: Application
@@ -33,6 +31,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/gke-dev/grafana-values.yaml
# Gitea: swap upc-dev → gke-dev
- target:
kind: Application
name: gitea
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/gke-dev/gitea-values.yaml
# OpenCost: swap upc-dev → gke-dev
- target:
kind: Application
@@ -41,3 +48,21 @@ patches:
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/gke-dev/opencost-values.yaml
# Secrets: change path to gke-dev
- target:
kind: Application
name: secrets
patch: |
- op: replace
path: /spec/source/path
value: secrets/gke-dev
# Enterprise-apps: point to gke-dev overlay
- target:
kind: Application
name: enterprise-apps
patch: |
- op: replace
path: /spec/source/path
value: apps/overlays/gke-dev

View File

@@ -1,18 +1,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/cert-manager-application
- ../../base/cluster-resources-application
- ../../base/grafana
- ../../base/grafana-dashboards
- ../../base/kyverno
- ../../base/kyverno-policies
- ../../base/loki
- ../../base/opencost
- ../../base/prometheus
- ../../base/sealedsecrets
- ../../base/tempo
- ../../base/traefik-application
- ../../base
patches:
# Traefik: swap upc-dev → gke-prod
@@ -24,6 +13,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/gke-prod/traefik-values.yaml
# Keycloak: swap upc-dev → gke-prod
- target:
kind: Application
name: keycloak
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/gke-prod/keycloak-values.yaml
# Grafana: swap upc-dev → gke-prod
- target:
kind: Application
@@ -33,6 +31,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/gke-prod/grafana-values.yaml
# Gitea: swap upc-dev → gke-prod
- target:
kind: Application
name: gitea
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/gke-prod/gitea-values.yaml
# OpenCost: swap upc-dev → gke-prod
- target:
kind: Application
@@ -41,3 +48,21 @@ patches:
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/gke-prod/opencost-values.yaml
# Secrets: change path to gke-prod
- target:
kind: Application
name: secrets
patch: |
- op: replace
path: /spec/source/path
value: secrets/gke-prod
# Enterprise-apps: point to gke-prod overlay
- target:
kind: Application
name: enterprise-apps
patch: |
- op: replace
path: /spec/source/path
value: apps/overlays/gke-prod

View File

@@ -1,15 +0,0 @@
---
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
creationTimestamp: null
name: microsoft-idp-credentials
namespace: keycloak
spec:
encryptedData:
MS_IDP_CLIENT_SECRET: AgBGeloOR8LVop8yluydjc7qj4JUkb85z7B3h07i3xLPup1ojgSqtx08huv+8gvSaFoPdxOY8g23iuKeAr2b2A70paHv0ILSVRsIpxzjuao4aDRWxMHD/SFzvitvqaXD1eQUcBNRDk/1NOG0o5b9o4ddMWkNmyCYuZcmOyx18DC1kFrcWGUgJkLTr+YRZcLJ2T80obZ+y4zztYc+vNQlu11KSIALz++c9XzK2XYSdOMdFHmzadWxoCjvkJqG4W5C6AQBlYa7NSydyU6K3TnwS4VHF8w1DRNneM/IuHLMNbcL8WZjHlSS8xgXFUMhG4rkejwM6joQLx57OosTQe1/xdCOPBXq4NO8Q76RXXkoI0EYMbzfK33vr4NVh8zmBUYxhaeQySh2jYdYp7t/79UzvP7jtGYUgqIZNEBqbDKkzvXsJ4dHJnss8U8lWVgLev31ZhaTjIr3A8VKDPbJKIRZPO71/4DiCF0WKLYNKfbYJ5tsyTxivFWDY5NPNkKkgXk4QoScZ3r4bYNZjDp1rC5zCRPGf0Lt1C5Zovx1HTUjpGpAFVi7DyNmEc0uXn2sTwPARAuAj8str+RnlRNzBkpPWKdlOoDVPTSA41dSTfUVZMlQlluu4fdDgGj5sEnhEN0iIxZNRwU/2DD2AVSqG+KtdIkfH6/j0jzdFn4D3Ha6vwAsBIURK4Ird/pLuNGGCDB+LrrNGXDTTKAPFydCuRtpyoM9kFOtSZb7T7q6Vfkqa7LfgP8mdG9JGXJx
template:
metadata:
creationTimestamp: null
name: microsoft-idp-credentials
namespace: keycloak

View File

@@ -2,16 +2,6 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- entra-upc-dev-credentials-sealed.yaml
# No patches needed — base already has "upc-dev" paths
# upc-dev is the default/base cluster
patches:
- target:
kind: Application
name: databunker
patch: |
- op: add
path: /spec/sources/0/helm/valueFiles/-
value: $values/infra/values/upc-dev/databunker-values.yaml

View File

@@ -1,21 +1,10 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base/cert-manager-application
- ../../base/cluster-resources-application
- ../../base/grafana
- ../../base/grafana-dashboards
- ../../base/kyverno
- ../../base/kyverno-policies
- ../../base/loki
- ../../base/opencost
- ../../base/prometheus
- ../../base/sealedsecrets
- ../../base/tempo
- ../../base/traefik-application
- ../../base
patches:
# Traefik: swap upc-dev → upc-prod
# Traefik: swap upc-dev → upc-prod in valueFiles
- target:
kind: Application
name: traefik
@@ -24,6 +13,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/upc-prod/traefik-values.yaml
# Keycloak: swap upc-dev → upc-prod
- target:
kind: Application
name: keycloak
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/upc-prod/keycloak-values.yaml
# Grafana: swap upc-dev → upc-prod
- target:
kind: Application
@@ -33,6 +31,15 @@ patches:
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/upc-prod/grafana-values.yaml
# Gitea: swap upc-dev → upc-prod
- target:
kind: Application
name: gitea
patch: |
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/upc-prod/gitea-values.yaml
# OpenCost: swap upc-dev → upc-prod
- target:
kind: Application
@@ -41,3 +48,21 @@ patches:
- op: replace
path: /spec/sources/0/helm/valueFiles/1
value: $values/infra/values/upc-prod/opencost-values.yaml
# Secrets: change path to upc-prod
- target:
kind: Application
name: secrets
patch: |
- op: replace
path: /spec/source/path
value: secrets/overlays/upc-prod
# Enterprise-apps: point to upc-prod overlay
- target:
kind: Application
name: enterprise-apps
patch: |
- op: replace
path: /spec/source/path
value: apps/overlays/upc-prod

View File

@@ -1,5 +0,0 @@
global:
domain: argocd.127.0.0.1.nip.io
notifications:
context:
clusterName: "aks-dev-launchpad"

View File

@@ -1,15 +0,0 @@
ingress:
main:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: start.forteapps.net
paths:
- path: /
pathType: Prefix
tls:
- secretName: homepage-tls
hosts:
- start.forteapps.net

View File

@@ -20,10 +20,55 @@ configs:
clientSecret: $oidc.clientSecret
requestedScopes: ["openid", "email", "profile"]
rbac:
# Base RBAC — org-wide roles shared across all clusters.
# Per-cluster policies go in infra/values/<cluster>/argocd-values.yaml
# as configs.rbac.policy.<cluster>.csv (ArgoCD concatenates all policy.*.csv keys)
policy.csv: |
# Platform administrators — full control
g, ArgoCD Admins, role:admin
# Read-only viewers — see all, change nothing
g, ArgoCD Viewers, role:readonly
# Deny users not in any declared KC group (ArgoCD Admins / ArgoCD Viewers)
# --- Per-team roles (scoped to default project app names) ---
# Observability team — manage monitoring stack
p, role:observability, applications, get, default/prometheus, allow
p, role:observability, applications, get, default/loki, allow
p, role:observability, applications, get, default/fluent-bit, allow
p, role:observability, applications, get, default/tempo, allow
p, role:observability, applications, get, default/grafana, allow
p, role:observability, applications, get, default/grafana-dashboards, allow
p, role:observability, applications, get, default/opencost, allow
p, role:observability, applications, sync, default/prometheus, allow
p, role:observability, applications, sync, default/loki, allow
p, role:observability, applications, sync, default/fluent-bit, allow
p, role:observability, applications, sync, default/tempo, allow
p, role:observability, applications, sync, default/grafana, allow
p, role:observability, applications, sync, default/grafana-dashboards, allow
p, role:observability, applications, sync, default/opencost, allow
p, role:observability, logs, get, default/*, allow
g, Observability Team, role:observability
# Dev tools team — manage gitea, renovate, karpor
p, role:devtools, applications, get, default/gitea, allow
p, role:devtools, applications, get, default/gitea-actions, allow
p, role:devtools, applications, get, default/renovate, allow
p, role:devtools, applications, get, default/karpor, allow
p, role:devtools, applications, sync, default/gitea, allow
p, role:devtools, applications, sync, default/gitea-actions, allow
p, role:devtools, applications, sync, default/renovate, allow
p, role:devtools, applications, sync, default/karpor, allow
p, role:devtools, logs, get, default/*, allow
g, Dev Tools Team, role:devtools
# App developers — manage enterprise apps only
p, role:app-dev, applications, get, default/enterprise-apps, allow
p, role:app-dev, applications, sync, default/enterprise-apps, allow
p, role:app-dev, applications, action, default/enterprise-apps, allow
p, role:app-dev, logs, get, default/enterprise-apps, allow
g, App Developers, role:app-dev
# Deny users not in any declared KC group
policy.default: ""
scopes: '[groups]'
params:
@@ -35,12 +80,6 @@ server:
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
gethomepage.dev/enabled: "true"
gethomepage.dev/name: "ArgoCD"
gethomepage.dev/description: "GitOps continuous delivery"
gethomepage.dev/group: "DevOps"
gethomepage.dev/icon: "argo-cd"
gethomepage.dev/href: "https://argocd.forteapps.net"
tls: true
extraArgs:
- --insecure

View File

@@ -1,42 +0,0 @@
# Default values for databunkerpro
image:
tag: 0.14.15
ingress:
enabled: false # Set to true to enable ingress
className: traefik
# Set host to enable ingress
host: databunker.example.com
annotations:
kubernetes.io/ingress.class: traefik
cert-manager.io/cluster-issuer: "letsencrypt-prod" # or your cluster issuer
traefik.ingress.kubernetes.io/ssl-redirect: "true"
traefik.ingress.kubernetes.io/force-ssl-redirect: "true"
traefik.ingress.kubernetes.io/ssl-passthrough: "false"
# Security headers
traefik.ingress.kubernetes.io/configuration-snippet: |
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
# TLS configuration
tls:
enabled: true # Set to true to enable TLS
secretName: "databunker-tls" # Name of the secret containing TLS certificate
# Pin PostgreSQL password — chart uses randAlphaNum without lookup,
# so each ArgoCD sync would regenerate the password while PVC keeps the old one.
# Same issue as Backstage PostgreSQL (see MEMORY.md).
internal:
postgresql:
auth:
password: "databunker-pg-pass-2026"
resources:
# Uncomment and adjust these values based on your requirements
# requests:
# memory: "512Mi"
# cpu: "250m"
# limits:
# memory: "1Gi"
# cpu: "500m"

View File

@@ -114,15 +114,6 @@ ingress:
className: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
gethomepage.dev/enabled: "true"
gethomepage.dev/name: "Gitea"
gethomepage.dev/description: "Git hosting & CI/CD"
gethomepage.dev/group: "DevOps"
gethomepage.dev/icon: "gitea"
gethomepage.dev/href: "https://git.forteapps.net"
gethomepage.dev/widget.type: "gitea"
gethomepage.dev/widget.url: "https://git.forteapps.net"
gethomepage.dev/widget.key: "{{HOMEPAGE_VAR_GITEA_TOKEN}}"
hosts:
- host: git.forteapps.net
paths:

View File

@@ -3,21 +3,11 @@ ingress:
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
gethomepage.dev/enabled: "true"
gethomepage.dev/name: "Grafana"
gethomepage.dev/description: "Metrics & observability dashboards"
gethomepage.dev/group: "Monitoring"
gethomepage.dev/icon: "grafana"
gethomepage.dev/href: "https://grafana.forteapps.net"
tls:
- secretName: grafana-tls
hosts:
- grafana.forteapps.net
persistence:
enabled: true
size: 1Gi
resources:
requests:
cpu: 50m

View File

@@ -1,72 +0,0 @@
# Homepage Helm Values
# Chart: jameswynn/homepage — https://gethomepage.dev
# Discovery: K8s service annotations (gethomepage.dev/*)
# Each deployed app annotates its own Service — apps not deployed = not visible.
# RBAC ClusterRole — required for cluster-wide service annotation scanning
enableRbac: true
serviceAccount:
create: true
name: homepage
config:
# Scan all namespaces for services with gethomepage.dev/enabled: "true"
kubernetes:
mode: cluster
traefik: true
settings:
title: "Platform"
headerStyle: clean
layout:
Apps:
style: row
columns: 3
Security:
style: row
columns: 3
Tools:
style: row
header: false
columns: 2
DevOps:
style: column
rows: 2
Monitoring:
style: column
rows: 1
# Top-of-page cluster overview widget
widgets:
- kubernetes:
cluster:
show: true
cpu: true
memory: true
showLabel: true
label: "Cluster"
nodes:
show: true
cpu: true
memory: true
showLabel: true
# In-cluster entries come from K8s service annotations.
# External (out-of-cluster) services are listed here statically.
bookmarks: []
services: []
resources:
requests:
cpu: 10m
memory: 128Mi
limits:
cpu: 100m
memory: 256Mi
env:
- name: HOMEPAGE_ALLOWED_HOSTS
value: start.forteapps.net
envFrom:
- secretRef:
name: homepage-widget-credentials

View File

@@ -18,12 +18,6 @@ ingress:
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
gethomepage.dev/enabled: "true"
gethomepage.dev/name: "Keycloak"
gethomepage.dev/description: "Identity & access management"
gethomepage.dev/group: "Security"
gethomepage.dev/icon: "keycloak"
gethomepage.dev/href: "https://id.forteapps.net/admin/forte-test/console/"
metrics:
enabled: true
@@ -181,6 +175,18 @@ keycloakConfigCli:
{
"name": "ArgoCD Viewers",
"path": "/ArgoCD Viewers"
},
{
"name": "Observability Team",
"path": "/Observability Team"
},
{
"name": "Dev Tools Team",
"path": "/Dev Tools Team"
},
{
"name": "App Developers",
"path": "/App Developers"
}
]
}
@@ -259,7 +265,7 @@ extraDeploy:
ADMIN_PASS=$(cat /secrets/admin-password)
echo "Authenticating to Keycloak..."
TOKEN=$(curl -s -X POST "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \
TOKEN=$(curl -sf -X POST "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \
-d "client_id=admin-cli" \
-d "username=${ADMIN_USER}" \
-d "password=${ADMIN_PASS}" \
@@ -276,7 +282,7 @@ extraDeploy:
upsert_secret() {
local ns="$1" name="$2" manifest="$3"
local code
code=$(curl -s -o /dev/null -w "%{http_code}" \
code=$(curl -sf -o /dev/null -w "%{http_code}" \
--cacert "$CA_CERT" \
-H "Authorization: Bearer ${SA_TOKEN}" \
-H "Content-Type: application/json" \
@@ -285,7 +291,7 @@ extraDeploy:
if [ "$code" = "200" ]; then
echo " Updated secret '${ns}/${name}'"
elif [ "$code" = "404" ]; then
code=$(curl -s -o /dev/null -w "%{http_code}" \
code=$(curl -sf -o /dev/null -w "%{http_code}" \
--cacert "$CA_CERT" \
-H "Authorization: Bearer ${SA_TOKEN}" \
-H "Content-Type: application/json" \
@@ -332,7 +338,7 @@ extraDeploy:
# Get the client secret from Keycloak
local secret_value
secret_value=$(curl -s -H "Authorization: Bearer ${TOKEN}" \
secret_value=$(curl -sf -H "Authorization: Bearer ${TOKEN}" \
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${client_uuid}/client-secret" \
| jq -r '.value')
@@ -347,7 +353,7 @@ extraDeploy:
# Write to target namespace (if it exists)
local ns_status
ns_status=$(curl -s -o /dev/null -w "%{http_code}" \
ns_status=$(curl -sf -o /dev/null -w "%{http_code}" \
--cacert "$CA_CERT" \
-H "Authorization: Bearer ${SA_TOKEN}" \
"${K8S_API}/api/v1/namespaces/${target_ns}")
@@ -371,12 +377,12 @@ extraDeploy:
local ns="$1" name="$2" key="$3" value="$4"
local patch
patch=$(printf '{"metadata":{"annotations":{"%s":"%s"}}}' "$key" "$value")
curl -s -o /dev/null \
curl -sf -o /dev/null \
--cacert "$CA_CERT" \
-H "Authorization: Bearer ${SA_TOKEN}" \
-H "Content-Type: application/strategic-merge-patch+json" \
-X PATCH -d "$patch" \
"${K8S_API}/api/v1/namespaces/${ns}/secrets/${name}" || true
"${K8S_API}/api/v1/namespaces/${ns}/secrets/${name}"
}
# =============================================
@@ -384,7 +390,7 @@ extraDeploy:
# =============================================
echo "=== Legacy sync: clients with k8s.secret.sync=true ==="
CLIENTS=$(curl -s -H "Authorization: Bearer ${TOKEN}" \
CLIENTS=$(curl -sf -H "Authorization: Bearer ${TOKEN}" \
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients")
SYNC_CLIENTS=$(echo "$CLIENTS" | jq -c '[.[] | select(.attributes["k8s.secret.sync"] == "true")]')
@@ -409,7 +415,7 @@ extraDeploy:
echo ""
echo "=== Self-service: config Secrets with label keycloak.forteapps.net/client-config=true ==="
CONFIG_SECRETS=$(curl -s \
CONFIG_SECRETS=$(curl -sf \
--cacert "$CA_CERT" \
-H "Authorization: Bearer ${SA_TOKEN}" \
"${K8S_API}/api/v1/namespaces/keycloak/secrets?labelSelector=keycloak.forteapps.net/client-config=true")
@@ -430,10 +436,6 @@ extraDeploy:
CLIENT_JSON=$(printf '%s' "$CLIENT_JSON_B64" | base64 -d)
CLIENT_ID=$(echo "$CLIENT_JSON" | jq -r '.clientId')
if [ -z "$CLIENT_ID" ] || [ "$CLIENT_ID" = "null" ]; then
echo "ERROR: Could not extract clientId from config '${CONFIG_NAME}', skipping"
continue
fi
echo "Processing self-service client '${CLIENT_ID}' from config '${CONFIG_NAME}'"
# Compute config hash for change detection
@@ -447,7 +449,7 @@ extraDeploy:
CRED_SECRET_KEY=$(echo "$CLIENT_JSON" | jq -r '.secret.keys.clientSecret // "client-secret"')
# Check if credential Secret already exists in target namespace
CRED_EXISTS=$(curl -s -o /dev/null -w "%{http_code}" \
CRED_EXISTS=$(curl -sf -o /dev/null -w "%{http_code}" \
--cacert "$CA_CERT" \
-H "Authorization: Bearer ${SA_TOKEN}" \
"${K8S_API}/api/v1/namespaces/${CRED_NS}/secrets/${CRED_NAME}")
@@ -470,17 +472,18 @@ extraDeploy:
publicClient: false,
redirectUris: .redirectUris,
webOrigins: .webOrigins,
defaultClientScopes: .defaultClientScopes,
protocolMappers: (.protocolMappers // [])
} + if .defaultClientScopes then {defaultClientScopes: .defaultClientScopes} else {} end')
}')
# Check if client already exists
EXISTING=$(curl -s -H "Authorization: Bearer ${TOKEN}" \
EXISTING=$(curl -sf -H "Authorization: Bearer ${TOKEN}" \
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients?clientId=${CLIENT_ID}" \
| jq -r '.[0].id // empty')
if [ -n "$EXISTING" ]; then
echo " Updating existing Keycloak client (uuid: ${EXISTING})"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-X PUT -d "$KC_CLIENT" \
@@ -493,7 +496,7 @@ extraDeploy:
CLIENT_UUID="$EXISTING"
else
echo " Creating new Keycloak client '${CLIENT_ID}'"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-X POST -d "$KC_CLIENT" \
@@ -504,37 +507,11 @@ extraDeploy:
continue
fi
# Fetch the newly created client's UUID
CLIENT_UUID=$(curl -s -H "Authorization: Bearer ${TOKEN}" \
CLIENT_UUID=$(curl -sf -H "Authorization: Bearer ${TOKEN}" \
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients?clientId=${CLIENT_ID}" \
| jq -r '.[0].id')
fi
# Assign default client scopes (KC REST API ignores defaultClientScopes in POST/PUT body)
REQUESTED_SCOPES=$(echo "$CLIENT_JSON" | jq -r '.defaultClientScopes // [] | .[]' 2>/dev/null)
if [ -n "$REQUESTED_SCOPES" ]; then
# Fetch all realm client scopes once
ALL_SCOPES=$(curl -s -H "Authorization: Bearer ${TOKEN}" \
"${KEYCLOAK_URL}/admin/realms/${REALM}/client-scopes")
echo "$REQUESTED_SCOPES" | while read -r SCOPE_NAME; do
[ -z "$SCOPE_NAME" ] && continue
SCOPE_ID=$(echo "$ALL_SCOPES" | jq -r --arg name "$SCOPE_NAME" '.[] | select(.name == $name) | .id // empty')
if [ -z "$SCOPE_ID" ]; then
echo " WARNING: Scope '${SCOPE_NAME}' not found in realm, skipping"
continue
fi
SC_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer ${TOKEN}" \
-X PUT \
"${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${CLIENT_UUID}/default-client-scopes/${SCOPE_ID}")
if [ "$SC_CODE" = "204" ] || [ "$SC_CODE" = "200" ]; then
echo " Assigned scope '${SCOPE_NAME}'"
else
echo " WARNING: Failed to assign scope '${SCOPE_NAME}' (HTTP ${SC_CODE})"
fi
done
fi
# Sync credentials to target namespace
sync_credentials "$CLIENT_ID" "$CLIENT_UUID" "$CRED_NS" "$CRED_NAME" "$CRED_ID_KEY" "$CRED_SECRET_KEY"

View File

@@ -1,36 +0,0 @@
# HashiCorp Vault Helm Chart Values
# Chart: hashicorp/vault v0.32.0
server:
standalone:
enabled: true
dataStorage:
enabled: true
size: 5Gi
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
ingress:
enabled: true
ingressClassName: traefik
pathType: Prefix
activeService: true
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
gethomepage.dev/enabled: "true"
gethomepage.dev/name: "Vault"
gethomepage.dev/description: "Secrets management"
gethomepage.dev/group: "Security"
gethomepage.dev/icon: "vault"
gethomepage.dev/href: "https://vault.forteapps.net"
ui:
enabled: true
serviceType: ClusterIP

View File

@@ -1,10 +0,0 @@
ingress:
enabled: true
host: databunker.forteapps.net
annotations:
gethomepage.dev/enabled: "true"
gethomepage.dev/name: "Databunker"
gethomepage.dev/description: "Secure Database for PII and PCI Records"
gethomepage.dev/group: "Security"
gethomepage.dev/icon: "double-take"
gethomepage.dev/href: "https://databunker.forteapps.net"

View File

@@ -1,65 +0,0 @@
ingress:
main:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: start.forteapps.net
paths:
- path: /
pathType: Prefix
tls:
- secretName: homepage-tls
hosts:
- start.forteapps.net
config:
settings:
title: "Forte Platform"
headerStyle: clean
layout:
Apps:
style: row
columns: 2
Security:
style: row
columns: 3
Tools:
style: row
header: false
columns: 2
DevOps:
style: column
rows: 2
Monitoring:
style: column
rows: 1
# Top-of-page cluster overview widget
widgets:
- kubernetes:
cluster:
show: true
cpu: true
memory: true
showLabel: true
label: "Cluster"
nodes:
show: true
cpu: true
memory: true
showLabel: true
# In-cluster entries come from K8s service annotations.
# External (out-of-cluster) services are listed here statically.
bookmarks: []
services:
- Apps:
- Forte Benken:
href: https://benken.hackathon.forteapps.net
description: Teknisk kompetanse fra offentlige anbud
icon: forte
- Forte Feedback:
href: https://feedback.forteapps.net
description: Fortes internal feedback app
icon: forte

View File

@@ -1,112 +1,2 @@
ingress:
hostname: id.forteapps.net
extraEnvVars:
- name: KC_FEATURES
value: "token-exchange:v1,admin-fine-grained-authz:v1"
keycloakConfigCli:
enabled: true
extraEnvVars:
- name: IMPORT_VAR_SUBSTITUTION_ENABLED
value: "true"
- name: MS_IDP_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: microsoft-idp-credentials
key: MS_IDP_CLIENT_SECRET
configuration:
microsoft-idp.json: |
{
"realm": "forte",
"authenticationFlows": [
{
"alias": "auto-link-first-broker-login",
"description": "Auto-link IdP accounts to existing users by email",
"providerId": "basic-flow",
"topLevel": true,
"builtIn": false,
"authenticationExecutions": [
{
"authenticator": "idp-create-user-if-unique",
"authenticatorFlow": false,
"requirement": "ALTERNATIVE",
"priority": 10
},
{
"authenticator": "idp-auto-link",
"authenticatorFlow": false,
"requirement": "ALTERNATIVE",
"priority": 20
}
]
}
],
"identityProviders": [
{
"alias": "forte-entra",
"displayName": "Forte Entra",
"providerId": "microsoft",
"enabled": true,
"trustEmail": true,
"firstBrokerLoginFlowAlias": "auto-link-first-broker-login",
"config": {
"clientId": "7995d2b5-b798-4caf-8da6-b00b78bb34d7",
"clientSecret": "$(env:MS_IDP_CLIENT_SECRET)",
"defaultScope": "openid email profile",
"tenantId": "063afd9e-5fcb-48d2-a769-ca31b0f5b443",
"syncMode": "IMPORT"
}
},
{
"alias": "forte-entra-graph",
"displayName": "Forte Entra (Graph)",
"providerId": "microsoft",
"enabled": true,
"storeToken": true,
"trustEmail": true,
"firstBrokerLoginFlowAlias": "auto-link-first-broker-login",
"config": {
"clientId": "7995d2b5-b798-4caf-8da6-b00b78bb34d7",
"clientSecret": "$(env:MS_IDP_CLIENT_SECRET)",
"defaultScope": "openid email profile User.Read Mail.Send",
"tenantId": "063afd9e-5fcb-48d2-a769-ca31b0f5b443",
"syncMode": "IMPORT"
}
}
],
"identityProviderMappers": [
{
"name": "forte-entra-email",
"identityProviderAlias": "forte-entra",
"identityProviderMapper": "hardcoded-attribute-idp-mapper",
"config": {
"syncMode": "INHERIT",
"attribute": "emailVerified",
"attribute.value": "true"
}
},
{
"name": "forte-entra-graph-email",
"identityProviderAlias": "forte-entra-graph",
"identityProviderMapper": "hardcoded-attribute-idp-mapper",
"config": {
"syncMode": "INHERIT",
"attribute": "emailVerified",
"attribute.value": "true"
}
}
],
"roles": {
"realm": [
{
"name": "default-roles-forte",
"composites": {
"client": {
"broker": ["read-token"]
}
}
}
]
}
}

View File

@@ -1,9 +0,0 @@
server:
ingress:
hosts:
- host: vault.forteapps.net
paths: []
tls:
- secretName: vault-tls
hosts:
- vault.forteapps.net

View File

@@ -0,0 +1,16 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- argocd-forte-helm-secret-sealed.yaml
- argocd-mcp-credentials.yaml
- argocdmcp-auth-oidc-sealed.yaml
- dot-ai-secrets.yaml
- forte10x-app-credentials-sealed.yaml
- gitea-backup-s3-sealed.yaml
- gitea-credentials-sealed.yaml
- gitea-runner-token-sealed.yaml
- gitea-smtp-secret-sealed.yaml
- keycloak-credentials-sealed.yaml
- musicman-credentials.yaml
- renovate-env-sealed.yaml
- ts-mcp-secrets-sealed.yaml

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