mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-04 19:17:20 +09:00
Introduces a new opt-in system package that provisions an admin-facing GitOps surface: a Flux GitRepository + Kustomization in cozy-system reconciling an admin-owned git repo of cluster customizations via a curated ServiceAccount. The admin uses this to manage Package CR patches (e.g. OIDC enable, MetalLB option overrides) and bring their own HelmReleases / Keycloak realm imports / NetworkPolicies as code in a repo they own, rather than via in-cluster kubectl patch. Wired into the platform chart via a single `customizer.enabled` flag in values; off by default. When enabled, emits the cozystack.customizer Package CR through the existing bundles/system.yaml path and respects bundles.disabledPackages. Curated ClusterRole grants patch (not delete) on packages.cozystack.io, read-only on platform HelmReleases, full ownership of PackageSources + admin-owned namespaces (default: cozy-customizer), and Keycloak realm CRs. Explicitly does NOT grant delete on Packages, anything on CRDs, the cozystack-controller Deployment, or kube-system. NOTE: this MVP ships the GitOps surface only. The field-ownership contract documented in the package values is currently advisory; a validating admission webhook on packages.cozystack.io is the planned enforcement (kustomize-controller hardcodes client.ForceOwnership on Apply, so SSA conflict detection cannot block silent ownership transfer at the Flux layer). Risk callout is inline in the values block. Includes helm-unittest coverage for the bundle wiring (off, on with values, disabledPackages override). Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
264 lines
12 KiB
YAML
264 lines
12 KiB
YAML
sourceRef:
|
|
kind: OCIRepository
|
|
name: cozystack-platform
|
|
namespace: cozy-system
|
|
path: /
|
|
migrations:
|
|
enabled: false
|
|
image: ghcr.io/cozystack/cozystack/platform-migrations:v1.4.0-rc.2@sha256:17390197a9b11d76a8dd1b24c6d1512adb7d3226b28b5bcba8551b14b1ec4be0
|
|
targetVersion: 42
|
|
# Bundle deployment configuration
|
|
bundles:
|
|
system:
|
|
enabled: false
|
|
variant: "isp-full" # Options: "isp-full", "isp-full-generic", "isp-hosted", "distro-full"
|
|
iaas:
|
|
enabled: false
|
|
paas:
|
|
enabled: false
|
|
naas:
|
|
enabled: false
|
|
disabledPackages: []
|
|
enabledPackages: []
|
|
# Network configuration
|
|
networking:
|
|
clusterDomain: "cozy.local"
|
|
podCIDR: "10.244.0.0/16"
|
|
podGateway: "10.244.0.1"
|
|
serviceCIDR: "10.96.0.0/16"
|
|
joinCIDR: "100.64.0.0/16"
|
|
# KubeOVN master nodes override (optional)
|
|
# By default, KubeOVN helm chart uses `lookup` to find control-plane nodes
|
|
# by label `node-role.kubernetes.io/control-plane`. On fresh clusters or
|
|
# during initial deployment, lookup may return empty results.
|
|
# Set this to comma-separated list of master node IPs to override.
|
|
kubeovn:
|
|
MASTER_NODES: ""
|
|
# Service publishing and ingress configuration
|
|
publishing:
|
|
host: "example.org"
|
|
ingressName: tenant-root
|
|
exposedServices:
|
|
- api
|
|
- dashboard
|
|
- vm-exportproxy
|
|
- cdi-uploadproxy
|
|
apiServerEndpoint: "" # example: "https://api.example.org"
|
|
externalIPs: []
|
|
# Enable PROXY-protocol on the host ingress-nginx and auto-deploy
|
|
# ouroboros (system/ouroboros) to fix the resulting hairpin-NAT
|
|
# problem (intra-cluster traffic to the cluster's own public hostnames
|
|
# arrives at ingress-nginx without the PROXY header it now requires).
|
|
# Single cozystack-side switch: flipping this to true configures both
|
|
# pieces in one go — ingress-nginx use-proxy-protocol and the
|
|
# cozystack.ouroboros Package CR.
|
|
#
|
|
# TALOS RE-RENDER NOTE (host scope only): the host CoreDNS is a
|
|
# Talos-managed Deployment; ouroboros mutates its live Corefile in
|
|
# place between BEGIN/END markers. A Talos machine-config push or
|
|
# upgrade re-renders the Corefile and overwrites the markers — there
|
|
# is a flap window of up to one ouroboros reconcile loop (bounded
|
|
# by `controller.resync`, default 10 minutes) until the controller
|
|
# re-applies the block. The reconcile loop is ALSO event-driven on
|
|
# Ingress/Gateway watches, so on a busy cluster the window often
|
|
# closes much sooner — but on a stable production cluster with no
|
|
# incidental Ingress/Gateway churn, the upper bound is the resync
|
|
# interval. In-cluster DNS for hairpinned hostnames briefly returns
|
|
# the upstream LB IP instead of the proxy ClusterIP during that
|
|
# window, which fails the same way pre-PR behaviour fails.
|
|
# Acceptable, but worth knowing during a Talos roll.
|
|
#
|
|
# PRECONDITION: the upstream L4 load balancer / WAN edge in front of
|
|
# ingress-nginx MUST already be injecting PROXY-protocol v1 headers
|
|
# before flipping this flag. Without that, every external request
|
|
# arrives at ingress-nginx without the PROXY header, ingress-nginx
|
|
# closes the connection, and ALL ingress traffic breaks. cozystack
|
|
# cannot configure that upstream LB for you — it lives outside the
|
|
# cluster (cloud LB, F5, MetalLB+haproxy, hcloud-cloud-controller,
|
|
# etc.). PROXY-protocol frames travel from the upstream LB to
|
|
# ingress-nginx (the LB opens a fresh TCP connection to the backend
|
|
# and prepends the frame on that connection), not back to the
|
|
# client — `nc` from a laptop will never see one. Pick one of:
|
|
#
|
|
# * tcpdump --interface any 'port 80 or port 443' -A on the node
|
|
# hosting ingress-nginx and watch for a leading "PROXY TCP4 ..."
|
|
# frame on inbound TCP from the LB BEFORE flipping this flag.
|
|
# * make any external HTTP request after the LB-side change but
|
|
# before flipping this flag, and watch
|
|
# `kubectl --namespace cozy-ingress-nginx logs deploy/ingress-nginx-controller`
|
|
# for "client sent invalid proxy protocol header" — that error
|
|
# means the LB started injecting PROXY frames but ingress-nginx
|
|
# is not yet configured to accept them, which is the transition
|
|
# state this flag is meant to resolve.
|
|
#
|
|
# If neither check shows PROXY frames, fix the upstream LB before
|
|
# touching this flag. Leave it false on clusters where PROXY-protocol
|
|
# is not yet wired at the upstream LB; ouroboros wastes resources
|
|
# when the bug it fixes does not exist.
|
|
#
|
|
# FAN-OUT NOTE: this flag affects ONLY the host control plane. To
|
|
# turn the same hairpin fix on inside a tenant cluster, also set
|
|
# `addons.ouroboros.enabled: true` in the per-tenant Kubernetes CR
|
|
# (and `addons.ingressNginx.enabled: true` is a hard precondition
|
|
# there). The flag does not cascade into tenants on its own.
|
|
#
|
|
# DISABLE PATH: flipping this back from true to false has TWO
|
|
# asymmetric effects. ingress-nginx side: the next reconcile re-emits
|
|
# cozystack.ingress-application without the use-proxy-protocol /
|
|
# real-ip-header / enable-real-ip overrides (cozystack deliberately
|
|
# never set use-forwarded-headers or compute-full-forwarded-for here
|
|
# — those would allow any upstream proxy to spoof X-Forwarded-For
|
|
# without a paired proxy-real-ip-cidr — see bundles/system.yaml),
|
|
# so ingress-nginx
|
|
# stops accepting PROXY-protocol headers. If the upstream L4 LB is
|
|
# still prepending PROXY frames at that moment, every external
|
|
# request to ingress-nginx breaks until the LB is also reconfigured
|
|
# — flip the LB OFF FIRST, then flip this flag. ouroboros side:
|
|
# stops emitting the cozystack.ouroboros Package CR, but every Package
|
|
# CR carries `helm.sh/resource-policy: keep`, so Helm leaves the
|
|
# existing Package on the cluster, ouroboros stays installed, and
|
|
# the live Corefile rewrite block keeps pointing at the still-running
|
|
# ouroboros-proxy Service. Flag-flip alone leaves ouroboros idempotent.
|
|
#
|
|
# To actually uninstall ouroboros, the operator removes the Package
|
|
# CR explicitly:
|
|
#
|
|
# kubectl delete package.cozystack.io cozystack.ouroboros
|
|
#
|
|
# or adds the package name to bundles.disabledPackages. THAT is the
|
|
# path that triggers the chart's pre-delete Helm hook
|
|
# (charts/ouroboros/templates/coredns-cleanup-hook.yaml, vendored at
|
|
# 0.7.0) — the hook quiesces the controller, sed-strips the
|
|
# BEGIN/END block from the live kube-system/coredns Corefile, and
|
|
# restores the cleaned ConfigMap via kubectl replace. The hook is
|
|
# automatic; operators do NOT need to run a manual sed recipe in
|
|
# the normal disable path. Documentation of the hook's exact
|
|
# behaviour lives in the upstream chart's README.
|
|
#
|
|
# Manual cleanup recipe (only if the chart's pre-delete hook fails
|
|
# to land — controller pod stuck in CrashLoop blocking the quiesce
|
|
# step, ConfigMap RBAC drift on the namespace, helm uninstall
|
|
# interrupted before the hook ran). Uses a JSON merge-patch so
|
|
# labels, annotations, and other data keys on the ConfigMap —
|
|
# including any Talos-managed metadata — are preserved:
|
|
#
|
|
# # === BEGIN cleanup-recipe ===
|
|
# existing=$(kubectl --namespace kube-system get configmap coredns \
|
|
# --output jsonpath='{.data.Corefile}')
|
|
# cleaned=$(printf '%s\n' "$existing" \
|
|
# | sed '/# === BEGIN ouroboros/,/# === END ouroboros ===/d')
|
|
# kubectl --namespace kube-system patch configmap coredns \
|
|
# --type merge \
|
|
# --patch "$(jq --null-input --arg c "$cleaned" \
|
|
# '{data: {Corefile: $c}}')"
|
|
# kubectl --namespace kube-system rollout restart deployment/coredns
|
|
# # === END cleanup-recipe ===
|
|
proxyProtocol: false
|
|
# Acknowledgement gate for the helm.sh/resource-policy: keep
|
|
# asymmetry (NOT a substitute for the chart's pre-delete cleanup
|
|
# hook — the hook handles the Corefile patch automatically when
|
|
# the chart actually uninstalls). The hazard this gate catches:
|
|
# flipping publishing.proxyProtocol from true to false stops
|
|
# emitting the cozystack.ouroboros Package CR, but resource-policy:
|
|
# keep means Helm leaves the existing Package on the cluster.
|
|
# ouroboros keeps running, the cleanup hook never fires, the
|
|
# rewrite block stays live. The platform render fails until the
|
|
# operator either restores proxyProtocol: true OR runs `kubectl
|
|
# delete package.cozystack.io cozystack.ouroboros` (which lets
|
|
# the chart actually uninstall and the hook run) and sets this
|
|
# flag to true to confirm. Once the cluster has been clean for
|
|
# one reconcile cycle, flip back to false.
|
|
#
|
|
# KNOWN HOLE: the guard keys off a `lookup` for the cozystack.ouroboros
|
|
# Package CR on the cluster. An operator who manually
|
|
# `kubectl delete package cozystack.ouroboros` before flipping the
|
|
# flag back bypasses the check entirely (the lookup returns nil, the
|
|
# render proceeds). In the chart 0.7.0 that path is mostly safe —
|
|
# `kubectl delete package` triggers helm uninstall and the chart's
|
|
# pre-delete hook patches the Corefile automatically. The hole is
|
|
# the rare case where the hook itself fails (controller pod stuck,
|
|
# ConfigMap RBAC drift, hook timeout) and the operator never
|
|
# noticed. The values comment + the acknowledge gate are
|
|
# defence-in-depth, not an airtight lock; an operator deliberately
|
|
# working around them gets to keep the broken pieces.
|
|
proxyProtocolAcknowledgeUnclean: false
|
|
certificates:
|
|
solver: http01 # "http01" or "dns01"
|
|
issuerName: letsencrypt-prod
|
|
# Authentication configuration
|
|
authentication:
|
|
oidc:
|
|
enabled: false
|
|
insecureSkipVerify: false
|
|
keycloakExtraRedirectUri: ""
|
|
# Internal URL to access KeyCloak realm for backend-to-backend requests (bypasses external DNS).
|
|
# When set, oauth2-proxy uses --skip-oidc-discovery and routes all backend calls (token, jwks,
|
|
# userinfo, logout) through this URL while keeping browser redirects on the external URL.
|
|
# Example: http://keycloak-http.cozy-keycloak.svc:8080/realms/cozy
|
|
keycloakInternalUrl: ""
|
|
# Pod scheduling configuration
|
|
scheduling:
|
|
globalAppTopologySpreadConstraints: ""
|
|
# UI branding configuration
|
|
branding: {}
|
|
# Container registry mirrors configuration
|
|
#
|
|
# Example:
|
|
# registries:
|
|
# mirrors:
|
|
# docker.io:
|
|
# endpoints:
|
|
# - http://10.0.0.1:8082
|
|
# ghcr.io:
|
|
# endpoints:
|
|
# - http://10.0.0.1:8083
|
|
# gcr.io:
|
|
# endpoints:
|
|
# - http://10.0.0.1:8084
|
|
# registry.k8s.io:
|
|
# endpoints:
|
|
# - http://10.0.0.1:8085
|
|
# quay.io:
|
|
# endpoints:
|
|
# - http://10.0.0.1:8086
|
|
# cr.fluentbit.io:
|
|
# endpoints:
|
|
# - http://10.0.0.1:8087
|
|
# docker-registry3.mariadb.com:
|
|
# endpoints:
|
|
# - http://10.0.0.1:8088
|
|
# config:
|
|
# "10.0.0.1:8082":
|
|
# tls:
|
|
# insecureSkipVerify: true
|
|
registries: {}
|
|
# Resource allocation ratios
|
|
resources:
|
|
cpuAllocationRatio: 10
|
|
memoryAllocationRatio: 1
|
|
ephemeralStorageAllocationRatio: 40
|
|
# Admin-facing GitOps customizer (cozystack.customizer).
|
|
# When enabled, a Flux GitRepository + Kustomization in cozy-system reconciles
|
|
# an admin-owned git repo of cluster customizations using a curated
|
|
# ServiceAccount. The admin pre-creates the git auth Secret named by
|
|
# source.secretRef in cozy-system before enabling.
|
|
#
|
|
# Risk callout: as of MVP there is no admission webhook on packages.cozystack.io.
|
|
# Customizer manifests can silently claim chart-owned fields on Package CRs
|
|
# (kustomize-controller force-transfers ownership unconditionally). Treat the
|
|
# field-ownership contract in operations/customizer as advisory until the
|
|
# webhook lands. Review customizer patches accordingly.
|
|
customizer:
|
|
enabled: false
|
|
source:
|
|
url: ""
|
|
ref:
|
|
branch: main
|
|
secretRef: ""
|
|
kustomization:
|
|
path: ""
|
|
ssaStrategy: Merge
|
|
rbac:
|
|
ownedNamespaces:
|
|
- cozy-customizer
|