Skip to content

feature: Add Public API Server Address Support#986

Open
rossigee wants to merge 26 commits into
clastix:masterfrom
rossigee:feature/public-address
Open

feature: Add Public API Server Address Support#986
rossigee wants to merge 26 commits into
clastix:masterfrom
rossigee:feature/public-address

Conversation

@rossigee

@rossigee rossigee commented Oct 9, 2025

Copy link
Copy Markdown

Add Public API Server Address Support

Overview

This PR introduces support for specifying a custom public API server address for TenantControlPlane instances. This allows using DNS hostnames in cluster-info ConfigMaps and kubeconfigs instead of LoadBalancer IPs, enabling better certificate SAN matching and avoiding x509 certificate errors.

Key Changes

API Changes

  • Added PublicAPIServerAddress field to ServiceSpec in the TenantControlPlane API
  • Field is optional and allows specifying a custom hostname for the API server
  • Updated CRDs and generated code (deepcopy, etc.)

Implementation

  • Added PublicControlPlaneAddress() method to TenantControlPlane struct
  • Method returns the public address if specified, otherwise falls back to assigned address
  • Optimized port handling using cmp.Or for default port (6443)
  • Integrated public address usage in kubeconfig generation for controller-manager and scheduler components
  • Post-processes generated kubeconfigs to replace server URLs with the public hostname

Tests

  • Added comprehensive unit tests for PublicControlPlaneAddress() method
  • Added E2E test validating the feature in cluster scenarios
  • Tests cover various scenarios: default/custom ports, address set/unset, error cases

Documentation

  • Updated API reference docs to include the new field
  • Added field description with usage examples

Usage Example

apiVersion: kamaji.clastix.io/v1alpha1
kind: TenantControlPlane
spec:
  controlPlane:
    service:
      serviceType: LoadBalancer
      publicAPIServerAddress: "k8s-api.example.com"
  # ... other spec

Benefits

  • Enables use of DNS names that match certificate SANs
  • Prevents x509 certificate errors in control plane components
  • Allows more flexible networking setups with custom hostnames

Breaking Changes

None. The field is optional and defaults to existing behavior.

Related Issues

@netlify

netlify Bot commented Oct 9, 2025

Copy link
Copy Markdown

Deploy Preview for kamaji-documentation canceled.

Name Link
🔨 Latest commit 2bea3f5
🔍 Latest deploy log https://app.netlify.com/projects/kamaji-documentation/deploys/68f33d9743b68e00089341f1

Comment thread charts/kamaji/crds/kamaji.clastix.io_datastores.yaml
@rossigee rossigee requested a review from prometherion October 18, 2025 07:11

@prometherion prometherion left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit lost here: it seems the proposed public API Server address is not pushed into the kubeadm functions, such as:

TenantControlPlaneEndpoint: r.getControlPlaneEndpoint(tenantControlPlane.Spec.ControlPlane.Ingress, address, port),

and the following one for kubeconfig

config.InitConfiguration.ControlPlaneEndpoint = fmt.Sprintf("%s.%s.svc:%d", tenantControlPlane.Name, tenantControlPlane.Namespace, tenantControlPlane.Spec.NetworkProfile.Port)

Comment thread Makefile Outdated
Comment on lines +20 to +26
// Fall back to the assigned control plane address, but use configured port
assignedAddress, _, err := in.AssignedControlPlaneAddress()
if err != nil {
return "", 0, err
}

return assignedAddress, port, nil

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Fall back to the assigned control plane address, but use configured port
assignedAddress, _, err := in.AssignedControlPlaneAddress()
if err != nil {
return "", 0, err
}
return assignedAddress, port, nil
// Fall back to the assigned control plane address
return in.AssignedControlPlaneAddress()

Unless I'm missing something, there's no need to replicate the same behavior of AssignedControlPlaneAddress.

Comment thread internal/resources/kubeconfig.go Outdated
Comment on lines +223 to +231
// Post-process the kubeconfig to use the public API server address for
// controller manager and scheduler components instead of localhost/IP address
if strings.Contains(r.KubeConfigFileName, "controller-manager") || strings.Contains(r.KubeConfigFileName, "scheduler") {
kubeconfig, kcErr = r.replaceServerURLWithPublicAddress(kubeconfig, tenantControlPlane)
if kcErr != nil {
logger.Error(kcErr, "cannot replace server URL in kubeconfig")
return kcErr
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I'm missing something, this is not required: Kamaji leverages the concept of using loopback to communicate with the API Server; otherwise, we would end up with extra network hops with potential egress traffic.

Comment thread internal/resources/kubeconfig.go Outdated
Comment on lines +272 to +280
func (r *KubeconfigResource) usePublicAPIServerAddress(config *kubeadm.Configuration, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
// Use the public API server endpoint configured in the TenantControlPlane
// instead of localhost for controller manager and scheduler kubeconfigs.
// This ensures that internal control plane components connect using the proper hostname
// which matches the certificate SANs, rather than failing with x509 certificate errors.

// Note: We don't modify the kubeadm configuration here because LocalAPIEndpoint.AdvertiseAddress
// must be an IP address according to kubeadm validation. Instead, we'll post-process
// the generated kubeconfig to replace the server URL with the hostname.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same discussion as we said before: Scheduler and Controller Manager must rely on the loopback address.

@prometherion

Copy link
Copy Markdown
Member

@rossigee I'm sorry the rebase will be painful, let me know if I can take care of that!

@rossigee rossigee force-pushed the feature/public-address branch from 2bea3f5 to eaffca1 Compare February 22, 2026 22:14
@netlify

netlify Bot commented Feb 22, 2026

Copy link
Copy Markdown

Deploy Preview for kamaji-documentation canceled.

Name Link
🔨 Latest commit 44e305e
🔍 Latest deploy log https://app.netlify.com/projects/kamaji-documentation/deploys/69ee891b1f27b800083e0462

rossigee added 20 commits April 26, 2026 16:28
Add PublicAPIServerAddress field to ServiceSpec for specifying custom API server hostnames.
Implement PublicControlPlaneAddress method to return public address with fallback to assigned address.
Integrate public address usage in kubeconfig generation for controller-manager and scheduler.
Add comprehensive tests for the functionality.
Add PublicAPIServerAddress field to TenantControlPlane ServiceSpec to allow
specifying a custom hostname for the API server. This enables using DNS names
that match certificate SANs instead of LoadBalancer IPs.

Key changes:
- Add PublicAPIServerAddress field to ServiceSpec
- Implement PublicControlPlaneAddress method with fallback logic
- Update CRDs and API documentation
- Remove kubeconfig post-processing for internal components (scheduler/controller-manager)
- Use controller-gen v0.16.1 as per project standards

The public address is used for external access (cluster-info ConfigMaps and user
kubeconfigs), while internal components continue to use loopback addresses.
…nfigs

- Add PublicControlPlaneAddress method to prioritize public address over assigned address
- Update kubeadm config generation to use public address for ControlPlaneEndpoint
- Automatically include public address in certificate SANs if not present
- Enhance e2e tests to validate kubeconfig server URLs use public address
- Fix lint issues and format code

This enables DNS-based cluster-info and kubeconfigs when PublicAPIServerAddress is specified.
…r-manager and scheduler kubeconfigs

- Controller-manager and scheduler now use cluster-local svc addresses in kubeconfigs
- Public address is still used for admin kubeconfigs and cluster-info
- Updated e2e tests to validate local endpoints for in-cluster components
- Update DeclaredControlPlaneAddress to return clusterIP when available for kubeadm compatibility
- Add nil check in worker kubeadm join test to prevent panic
- Fix import ordering in public address test
Add nil check for stdout before io.ReadAll to prevent panic.
Check for nil data and config fields before accessing them.
Check for AlreadyExists error when creating GatewayClass to allow reruns.
Add nil check for stdout before io.ReadAll.
Check if cluster exists in config before accessing it.
This avoids panics and provides clear failure messages.
Use context.Cluster to get the cluster name from kubeconfig.
Update customizeConfig to use full DNS with cluster.local, and update test expectations.
Update customizeConfig and test expectations to match original behavior.
Update expectations to match the public address when set.
…ager and scheduler kubeconfigs

Only override to internal DNS if PublicAPIServerAddress is not set.
Kubeconfig cluster name is always 'kubernetes' in the Clusters map.
The cluster key is context.Cluster, not hardcoded 'kubernetes'.
The kubeconfig cluster name is always 'kubernetes'.
The kubeconfig cluster name is set to the TCP name.
The exec may return err even when command succeeds, check exitCode for proper failure detection.
The kubeconfig cluster name is always 'kubernetes'.
This should work regardless of the cluster name.
Return PublicAPIServerAddress if set, for proper endpoint configuration.
- Always use cluster-local Service addresses for internal components
- Avoid unnecessary network hops through external LoadBalancer
- Update tests to verify local addresses instead of public address
@rossigee rossigee force-pushed the feature/public-address branch from d3e5503 to 44e305e Compare April 26, 2026 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants