Merge branch 'main' of https://git.forteapps.net/Forte/launchpad into feature/tofu
All checks were successful
AI Code Review / ai-review (pull_request) Successful in 7s
All checks were successful
AI Code Review / ai-review (pull_request) Successful in 7s
This commit is contained in:
@@ -738,6 +738,59 @@ 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`
|
||||
@@ -1023,6 +1076,52 @@ dind:
|
||||
- Gitea admin panel (`/admin/runners`) — runners show as Online
|
||||
- Create test workflow in `.gitea/workflows/test.yml` — job executes
|
||||
|
||||
### Vaultwarden
|
||||
|
||||
**Chart**: `guerzon/vaultwarden`
|
||||
**Version**: 0.36.4 (app v1.36.0-alpine)
|
||||
**Namespace**: `vaultwarden`
|
||||
|
||||
**Purpose**: Self-hosted Bitwarden-compatible password manager.
|
||||
|
||||
**Configuration**:
|
||||
```yaml
|
||||
# infra/overlays/upc-dev/vaultwarden/ + infra/values/
|
||||
domain: "https://bitwarden.forteapps.net"
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
class: "traefik"
|
||||
tls: true
|
||||
tlsSecret: vaultwarden-tls
|
||||
hostname: bitwarden.forteapps.net
|
||||
additionalAnnotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
|
||||
database:
|
||||
type: postgresql
|
||||
host: vaultwarden-postgresql # StatefulSet in overlay
|
||||
existingSecret: prod-db-creds
|
||||
|
||||
storage:
|
||||
data: 5Gi (ReadWriteOnce)
|
||||
attachments: 5Gi (ReadWriteOnce)
|
||||
```
|
||||
|
||||
**TLS**: cert-manager auto-provisions Let's Encrypt certificate via `letsencrypt-prod` ClusterIssuer (same pattern as Gitea, Grafana, etc).
|
||||
|
||||
**SSO**: Keycloak OIDC via `forte` realm (client ID: `vaultwarden`). Self-service client config Secret (`keycloak-client-vaultwarden`) triggers registrar to create KC client and sync credentials to `vaultwarden-oidc-credentials`. PKCE enabled.
|
||||
|
||||
**Endpoints**:
|
||||
- Web UI: `https://bitwarden.forteapps.net`
|
||||
|
||||
**Database**: Separate ArgoCD Application `vaultwarden-postgresql` (sync-wave `"0"`) deploys PostgreSQL 16 StatefulSet + SealedSecret before Vaultwarden (wave `"1"`). 2Gi PVC. Chart does NOT include a PostgreSQL subchart — must be provisioned separately.
|
||||
|
||||
**Secrets**:
|
||||
- `prod-db-creds` (SealedSecret) — PostgreSQL credentials (`pgusername`, `pgpassword`) + SMTP credentials
|
||||
- `vaultwarden-oidc-credentials` (registrar-managed) — OIDC client ID + secret
|
||||
- `vaultwarden-tls` — auto-managed by cert-manager
|
||||
|
||||
### AI Code Review (ai-review)
|
||||
|
||||
**Type**: Gitea Actions workflow (`.gitea/workflows/ai-review.yaml`)
|
||||
@@ -1101,6 +1200,30 @@ ignore:
|
||||
- Check Gitea Actions tab for workflow run status and logs
|
||||
- Monitor Anthropic usage dashboard for token consumption
|
||||
|
||||
### Keycloak Browser Flow (IdP Auto-Redirect)
|
||||
|
||||
**File**: `infra/values/base/keycloak-values.yaml` (inside `forte-realm.json`)
|
||||
|
||||
The realm uses a custom browser authentication flow (`browser-auto-idp`) that skips the Keycloak login page and redirects directly to the Entra ID identity provider.
|
||||
|
||||
**Flow executions**:
|
||||
|
||||
| Priority | Authenticator | Requirement | Purpose |
|
||||
|----------|--------------|-------------|---------|
|
||||
| 10 | `auth-cookie` | ALTERNATIVE | Reuse existing session (no redirect) |
|
||||
| 20 | `identity-provider-redirector` | ALTERNATIVE | Auto-redirect to `forte-entra` IdP |
|
||||
|
||||
**Key fields in realm JSON**:
|
||||
- `"browserFlow": "browser-auto-idp"` — overrides the default `browser` flow at realm level
|
||||
- `"authenticationFlows"` — defines the custom flow with its executions
|
||||
- `"authenticatorConfig"` — sets `defaultProvider: "forte-entra"` on the redirector
|
||||
|
||||
**Why custom flow**: The default KC browser flow shows a username/password form with an IdP button. Since all authentication is via Entra ID, the custom flow eliminates this step. The `auth-cookie` execution preserves session reuse so returning users aren't redirected again.
|
||||
|
||||
**Important**: The `forte-entra` identity provider must exist in Keycloak (currently configured manually in the KC admin console). If the IdP alias changes, update the `defaultProvider` value in the realm JSON.
|
||||
|
||||
---
|
||||
|
||||
### Keycloak Client Registrar
|
||||
|
||||
**Type**: CronJob (deployed via Keycloak Helm chart `extraDeploy`)
|
||||
@@ -1132,9 +1255,18 @@ ignore:
|
||||
|
||||
**Resources**:
|
||||
- `ServiceAccount`: `keycloak-client-registrar` (namespace: `keycloak`)
|
||||
- `ClusterRole`: `keycloak-client-registrar` (secrets: get/list/create/update/patch; namespaces: get/list)
|
||||
- `ClusterRole`: `keycloak-client-registrar`
|
||||
- Secrets: `get`, `list`, `create`, `update`, `patch`
|
||||
- Namespaces: `get`, `list`
|
||||
- `ClusterRoleBinding`: `keycloak-client-registrar`
|
||||
- `CronJob`: `keycloak-client-registrar`
|
||||
- **Schedule**: `*/2 * * * *` (every 2 minutes)
|
||||
- **Concurrency Policy**: `Forbid` (prevents concurrent runs)
|
||||
- **Backoff Limit**: 3 retries per job
|
||||
- **History**: 1 successful job, 3 failed jobs retained
|
||||
- **Resources**: 50m CPU / 64Mi memory (requests), 200m CPU / 128Mi memory (limits)
|
||||
|
||||
**Container**: Alpine 3.20 with `curl` and `jq` installed
|
||||
|
||||
**Kyverno Policy**: `keycloak-client-config-cloner` — clones labeled Secrets from app namespaces to `keycloak` namespace (see [Kyverno Policies](#kyverno-policies))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user