cozystack/packages/core/platform/values.yaml
Aleksei Sviridkin adc7abe5c1
feat(ingress): add loadBalancer exposure mode via CiliumLoadBalancerIPPool
Service.spec.externalIPs is deprecated upstream in Kubernetes v1.36
(KEP-5707, kubernetes#137293). The AllowServiceExternalIPs feature gate
is expected to default to off around v1.40 and the implementation to
be removed around v1.43. For bare-metal installs that rely on
externalIPs today, cozystack needs a migration path.

This change adds an opt-in 'loadBalancer' exposure mode for the
ingress-nginx Service:

- New platform value 'publishing.exposure' (enum: externalIPs |
  loadBalancer, default externalIPs). Plumbed through cozystack-values
  into each tenant's ingress HelmRelease via the new 'expose-mode' key.
- Unknown values and loadBalancer with an empty externalIPs list fail
  the chart render with explicit error messages, rather than silently
  producing a broken Service.
- When exposure=loadBalancer and the current namespace matches
  publishing.ingressName, the Service becomes type: LoadBalancer with
  externalTrafficPolicy: Local.
- A new template renders a CiliumLoadBalancerIPPool whose blocks come
  from publishing.externalIPs (IPv4 addresses get /32, IPv6 addresses
  get /128) and whose serviceSelector uses Cilium's synthetic
  io.kubernetes.service.namespace key combined with the standard
  app.kubernetes.io/name: ingress-nginx label. No custom label is
  written to the Service itself, avoiding cross-tenant collisions from
  user-defined labels.

Default behaviour is unchanged: without opting in, the Service is
still ClusterIP + spec.externalIPs as today.

Scope: only ingress-nginx is migrated by this setting. Other cozystack
components that still write Service.spec.externalIPs directly (notably
the vpn app) must be migrated separately before the v1.40 feature gate
flip.

Tests: packages/extra/ingress/tests/exposure_test.yaml adds 13
helm-unittest cases covering both modes, IPv4/IPv6, empty-token
filtering, unknown-mode rejection, and the non-matching-namespace
fallback.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
2026-04-23 17:35:36 +03:00

137 lines
5.1 KiB
YAML

sourceRef:
kind: OCIRepository
name: cozystack-platform
namespace: cozy-system
path: /
migrations:
enabled: false
image: ghcr.io/cozystack/cozystack/platform-migrations:v1.3.0@sha256:555e4b76421361805a84bc9088b01b23a9c4a9430bd8ebd2db82ef9677d7008c
targetVersion: 39
# 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: []
# Exposure mode for the ingress-nginx Service. When "externalIPs" (current
# default) is selected, the Service is created as ClusterIP with
# Service.spec.externalIPs set from publishing.externalIPs. When
# "loadBalancer" is selected, the Service is type: LoadBalancer and a
# CiliumLoadBalancerIPPool makes those same addresses allocatable via LB IPAM.
#
# Service.spec.externalIPs is deprecated upstream in Kubernetes v1.36
# (KEP-5707). The AllowServiceExternalIPs feature gate is expected to default
# to false around v1.40 and the implementation removed around v1.43 — switch
# to "loadBalancer" before upgrading past v1.40.
#
# Caveats for the "loadBalancer" mode:
# - publishing.externalIPs must contain at least one non-empty address,
# otherwise the chart render fails with an explicit error (a LoadBalancer
# Service without a pool would sit in <pending> forever).
# - The ingress-nginx Service is created with externalTrafficPolicy: Local
# to preserve the client source IP. Traffic arriving on a node that does
# not host an ingress-nginx pod is dropped, so the external IP must be
# routed to a node that runs the ingress pod (floating IP / keepalived /
# upstream router / podAntiAffinity).
# - Cilium does NOT announce the IP on its own unless L2 announcements or
# BGP are enabled in the Cilium values (disabled by default in Cozystack).
# This mode assumes the operator already routes the externalIPs to a
# cluster node; enabling announcements is out of scope for this setting.
# - Switching this value on a running cluster causes the ingress-nginx
# Service to be recreated (the HelmRelease has upgrade.force: true and
# the Service kind changes between ClusterIP and LoadBalancer). Expect a
# brief interruption of ingress traffic during the flip.
#
# Scope: this setting only controls the ingress-nginx Service. Other
# cozystack components that currently write Service.spec.externalIPs directly
# (e.g. the vpn app at packages/apps/vpn/templates/service.yaml) are NOT
# migrated by flipping this value and must be addressed separately before
# the AllowServiceExternalIPs feature gate flips to off in ~v1.40.
exposure: externalIPs # "externalIPs" or "loadBalancer"
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