publishing.host | "example.org" | Основной домен для всех сервисов, создаваемых в Cozystack: dashboard, Grafana, Keycloak и других. |
publishing.apiServerEndpoint | "" | Используется для генерации kubeconfig-файлов для пользователей. Рекомендуется использовать маршрутизируемый FQDN или IP-адрес вместо адресов, доступных только локально. Пример: "https://api.example.org". |
publishing.exposedServices | [api, dashboard, vm-exportproxy, cdi-uploadproxy] | Список сервисов для публикации. Возможные значения: api, dashboard, cdi-uploadproxy, vm-exportproxy. |
publishing.ingressName | "tenant-root" | Ingress controller, используемый для публикации сервисов. |
publishing.externalIPs | [] | Список external IP, используемых указанным ingress controller. Если не задан, по умолчанию используется сервис LoadBalancer. |
publishing.ingressNameAdmin | "" | Имя отдельного административного ingress, к которому системные компоненты подключают свои административные маршруты (на данный момент только Keycloak) — например, привязанного к приватному IP и недоступного из интернета. На пути через Gateway значение — это namespace (для Gateway с именем cozystack); на пути через ingress-nginx это ingressClassName. Эти два варианта совпадают, потому что Cozystack называет каждый ingressClass по имени namespace тенанта. Пустое значение (по умолчанию) оставляет административные эндпоинты на publishing.ingressName. Два предостережения: Cozystack не создаёт административный Gateway — его нужно предварительно подготовить отдельно, иначе административные маршруты окажутся ни к чему не подключены; а также административные маршруты рендерятся только когда у Keycloak установлен ingress.adminHost, поэтому установка одного этого ключа сама по себе ничего не даёт. |
publishing.exposureClass.name | "" | Логическое имя кластерного ресурса ExposureClass, который нужно отрендерить. Пустое значение (по умолчанию) оставляет путь publishing.externalIPs без изменений. Когда значение задано, host ingress — при пустом publishing.externalIPs — публикует свой Service как type: LoadBalancer, а cozystack-controller транслирует класс в пул VIP выбранного backend и его анонсировщик. Это путь миграции с Service.spec.externalIPs, устаревшего начиная с Kubernetes v1.36 (KEP-5707). Удаление происходит поэтапно: feature gate AllowServiceExternalIPs поставляется включённым в v1.36, затем по умолчанию становится false (цель KEP: v1.40), после чего kube-proxy перестаёт программировать правила для этого поля — API всё ещё принимает его, поэтому Service выглядит здоровым, хотя трафик никуда не приходит. На последующих этапах gate блокируется в выключенном состоянии, а поддержка в kube-proxy полностью удаляется, и в итоге сам gate и admission-контроллер DenyServiceExternalIPs убираются. KEP указывает эти более поздние релизы как приблизительные, а объявление для v1.36 обещает лишь «будущий минорный релиз», поэтому переключение значения по умолчанию на false стоит воспринимать как дедлайн и мигрировать на класс до этого обновления, а не ориентироваться на конкретную версию. |
publishing.loadBalancerClass | "" | Native loadBalancerClass for the host ingress. When publishing.externalIPs is empty the host ingress publishes its Service as type: LoadBalancer; this is the loadBalancerClass of the LoadBalancer controller that should handle it. The admin provisions the matching address pool or announcer out of band (a MetalLB IPAddressPool, a Cilium LB-IPAM pool, a cloud LB). Empty (the default) lets the cluster’s default LoadBalancer controller claim the Service. spec.loadBalancerClass is immutable: on a cluster whose host ingress Service already exists as type: LoadBalancer, setting, changing or clearing this value requires recreating that Service once. Until then the ingress HelmRelease in the host ingress namespace fails to reconcile, and its error carries the exact kubectl delete service command. With a non-empty publishing.externalIPs the host ingress Service stays ClusterIP and this value is ignored without an error. This is the migration path off Service.spec.externalIPs, deprecated in Kubernetes v1.36 (KEP-5707). The removal is phased: the AllowServiceExternalIPs feature gate ships enabled in v1.36, then defaults to false (KEP target: v1.40), at which point kube-proxy stops programming rules for the field — the API still accepts it, so the Service looks healthy while no traffic arrives. Later phases lock the gate off and strip kube-proxy’s support outright, and finally drop the gate and the DenyServiceExternalIPs admission controller. The KEP gives those later releases as approximate and the v1.36 announcement commits only to “a future minor release”, so treat the default flipping to false as the deadline and move the host ingress off publishing.externalIPs before that upgrade rather than planning against a specific version. |
publishing.exposureClass.backend | "externalIPs" | Механизм LoadBalancer, стоящий за классом. externalIPs фиксирует IP узлов без пула или анонсировщика (историческое поведение); metallb рендерит IPAddressPool + L2Advertisement; cilium рендерит CiliumLoadBalancerIPPool + CiliumL2AnnouncementPolicy; robotlb использует облачный (Hetzner) load balancer, где выделение и анонсирование происходят вне кластера. Как только publishing.exposureClass.name задан, backend с пулом или облачный backend (metallb, cilium, robotlb) взаимно исключает непустой publishing.externalIPs — рендер платформы завершится явной ошибкой, а не отправит нерабочий вариант в production, потому что Service host ingress остался бы ClusterIP + externalIPs, никогда не стал бы type: LoadBalancer, а выделенный пул остался бы неиспользованным. |
publishing.exposureClass.addresses | [] | VIP CIDR или диапазоны вида start-end для backend’ов с пулом (metallb, cilium). |
publishing.exposureClass.l2 | true | Анонсирование L2 (ARP / NDP) для bare-metal backend’ов. |
publishing.exposureClass.isDefault | true | Делает этот класс используемым по умолчанию для любого ServiceExposure, который не указывает имя класса. ServiceExposure — это внутрикластерный ресурс, который cozystack-controller реконсилирует, чтобы опубликовать Service через backend класса; рабочие нагрузки не создают его напрямую. Поскольку по умолчанию это значение true, ручное создание второго ExposureClass без снятия у него флага default оставляет два класса по умолчанию, и контроллер тогда завершает с ошибкой AmbiguousDefaultClass каждый ServiceExposure без указанного имени — необходимо поддерживать ровно один класс по умолчанию. |
publishing.certificates.solver | "http01" | Тип ACME challenge solver для letsencrypt issuer по умолчанию. Возможные значения: http01, dns01. |
publishing.certificates.issuerName | "letsencrypt-prod" | Имя ClusterIssuer для TLS-сертификатов, используемых в системных Helm releases. |
publishing.certificates.wildcard | false | Опциональное включение общего wildcard-сертификата на пути ingress-nginx по умолчанию (gateway.enabled=false). Когда значение true при solver=dns01 и без указания publishing.certificates.wildcardSecretName, платформа выпускает один Certificate вида *.<root-host> + <root-host> через DNS-01 ClusterIssuer и использует его как SSL-сертификат по умолчанию для ingress-контроллера — таким образом системные сервисы перестают выпускать отдельный ACME-сертификат на каждый хост, что позволяет избежать лимитов Let’s Encrypt при масштабировании (аналогично поведению на пути Gateway API). Игнорируется при http01 (wildcard-сертификаты выпускать нельзя) и при gateway.enabled=true (в этом случае wildcard-сертификат выпускает контроллер TenantGateway). При включении имя выпущенного Secret передаётся в тот же канал значений кластера, что и publishing.certificates.wildcardSecretName, поэтому возникает та же проблема с дочерними тенантами и та же открытая ошибка (
cozystack/cozystack#3296): у всех тенантов системные ingress-ресурсы во всём кластере теряют собственный ACME-сертификат на каждый хост, но wildcard-сертификат обслуживается только контроллером публикации. Дочерний тенант, запускающий собственный ingress-контроллер (ingress: true), в результате остаётся без сертификата и обслуживается встроенным самоподписанным сертификатом ingress-nginx; наследующий дочерний тенант на общем контроллере вместо этого получает несовпадение имени хоста, поскольку однокомпонентный wildcard *.<root-host> не покрывает вложенный хост вида <service>.<tenant>.<root-host> или произвольный ingress.host на другом домене. Включайте эту опцию только тогда, когда покрыты все публикуемые хосты и ни один дочерний тенант не запускает собственный ingress-контроллер. По умолчанию отключено, чтобы кластер с dns01 никогда не переключался незаметно при обновлении. |
publishing.certificates.wildcardSecretName | "" | Wildcard TLS Secret, предоставленный оператором. Если задан, системные сервисы платформы и ingress/Gateway корневого тенанта используют этот заранее существующий Secret вместо выпуска отдельных ACME-сертификатов на каждый хост (по каналу значений передаётся только ИМЯ — но никогда сам ключевой материал), и этот параметр имеет приоритет над publishing.certificates.wildcard. Secret должен существовать в namespace публикации (по умолчанию tenant-root), содержать корректный PEM в полях tls.crt / tls.key (создан как kubernetes.io/tls, хотя проверяется только сам материал) и покрывать обслуживаемые хосты. Область действия ограничена корневым тенантом, но это не проверяется принудительно — имя достигает всех тенантов, из-за чего дочерний тенант, запускающий собственный ingress-контроллер, может остаться с самоподписанным сертификатом (
cozystack/cozystack#3296). Полное описание поведения и трёх случаев для дочерних тенантов см. в разделе
Gateway API → Certificates. Оставьте пустым, чтобы сохранить выпуск сертификатов через ACME. |
publishing.certificates.dns01.provider | "cloudflare" | DNS-01 provider, когда solver=dns01. Возможные значения: cloudflare, route53, digitalocean, rfc2136. Это значение читают и per-tenant Issuer (формируемый cozystack-controller из CR TenantGateway), и общекластерные ClusterIssuer letsencrypt-prod / letsencrypt-stage, используемые прежним ingress-механизмом. |
publishing.certificates.dns01.cloudflare.secretName | "cloudflare-api-token-secret" | Имя Secret с Cloudflare API token, имеющим права Zone:Read + Zone:DNS:Edit на apex-зону. |
publishing.certificates.dns01.cloudflare.secretKey | "api-token" | Ключ внутри Secret, содержащий API token. |
publishing.certificates.dns01.route53.region | "" | AWS-регион hosted zone Route53. Требуется, когда provider=route53. |
publishing.certificates.dns01.route53.accessKeyID | "" | IAM access key ID. Необязателен при работе с IRSA / instance profile. |
publishing.certificates.dns01.route53.secretName | "" | Имя Secret с IAM secret access key. Необязателен при работе с IRSA / instance profile. |
publishing.certificates.dns01.route53.secretKey | "secret-access-key" | Ключ внутри Secret Route53, содержащий secret access key. |
publishing.certificates.dns01.digitalocean.secretName | "digitalocean-api-token-secret" | Имя Secret с DigitalOcean API token, имеющим доступ на запись к apex-домену. |
publishing.certificates.dns01.digitalocean.secretKey | "access-token" | Ключ внутри Secret, содержащий DigitalOcean token. |
publishing.certificates.dns01.rfc2136.nameserver | "" | host:port авторитетного nameserver, принимающего динамические обновления RFC 2136. Требуется, когда provider=rfc2136. |
publishing.certificates.dns01.rfc2136.tsigKeyName | "" | Имя TSIG-ключа, авторизующего динамические обновления. Требуется, когда provider=rfc2136. |
publishing.certificates.dns01.rfc2136.tsigAlgorithm | "HMACSHA256" | HMAC-алгоритм TSIG. |
publishing.certificates.dns01.rfc2136.secretName | "" | Имя Secret с материалом TSIG-ключа. Требуется, когда provider=rfc2136. |
publishing.certificates.dns01.rfc2136.secretKey | "tsig-secret-key" | Ключ внутри Secret, содержащий TSIG-ключ. |
publishing.proxyProtocol | false | Включает PROXY-протокол на хост-инстансе ingress-nginx и автоматически развёртывает
ouroboros для устранения возникающей проблемы hairpin-NAT. Вышестоящий L4-балансировщик перед ingress-nginx должен уже внедрять заголовки PROXY-v1 до включения этого флага; см. связанную страницу для рецептов проверки и пути отключения. |
publishing.proxyProtocolAcknowledgeUnclean | false | Флаг подтверждения для асимметрии helm.sh/resource-policy: keep на пути отключения хоста. Переключение publishing.proxyProtocol с true обратно на false прекращает генерацию Package CR cozystack.ouroboros, но не удаляет уже существующий — рендеринг платформы завершается с ошибкой до тех пор, пока либо Package CR не будет удалён (что запускает pre-delete cleanup хук чарта), либо этот флаг не будет установлен в true, подтверждая, что оператор обработал асимметрию. См.
hairpin-proxy-protocol → Disable path для полной последовательности действий. |