diff --git a/Makefile b/Makefile index 126bb609..b1ccb3fd 100644 --- a/Makefile +++ b/Makefile @@ -170,10 +170,10 @@ BUILD_DATE ?= $$(git log -1 --format="%at" | xargs -I{} date -d @{} +%Y-%m- get_version: @echo -n v$(VERSION) -build: generate fmt vet ## Build manager binary. +build: generate golint ## Build manager binary. go build -o bin/manager main.go -run: manifests generate fmt vet ## Run a controller from your host. +run: manifests generate ## Run a controller from your host. go run ./main.go docker-build: ## Build docker image with the manager. diff --git a/api/v1alpha1/tenantcontrolplane_funcs.go b/api/v1alpha1/tenantcontrolplane_funcs.go index b2276f90..bd30a962 100644 --- a/api/v1alpha1/tenantcontrolplane_funcs.go +++ b/api/v1alpha1/tenantcontrolplane_funcs.go @@ -61,10 +61,35 @@ func (in *TenantControlPlane) DeclaredControlPlaneAddress(ctx context.Context, c return "", kamajierrors.NonExposedLoadBalancerError{} } - for _, lb := range loadBalancerStatus.Ingress { - if ip := lb.IP; len(ip) > 0 { - return ip, nil - } + return getLoadBalancerAddress(loadBalancerStatus.Ingress) + } + + return "", kamajierrors.MissingValidIPError{} +} + +// getLoadBalancerAddress extracts the IP address from LoadBalancer ingress. +// It also checks and rejects hostname usage for LoadBalancer ingress. +// +// Reasons for not supporting hostnames: +// - DNS resolution can differ across environments, leading to inconsistent behavior. +// - It may cause connectivity problems between Kubernetes components. +// - The DNS resolution could change over time, potentially breaking cluster-to-API-server connections. +// +// Recommended solutions: +// - Use a static IP address to ensure stable and predictable communication within the cluster. +// - If a hostname is necessary, consider setting up a Virtual IP (VIP) for the given hostname. +// - Alternatively, use an external load balancer that can provide a stable IP address. +// +// Note: Implementing L7 routing with the API Server requires a deep understanding of the implications. +// Users should be aware of the complexities involved, including potential issues with TLS passthrough +// for client-based certificate authentication in Ingress expositions. +func getLoadBalancerAddress(ingress []corev1.LoadBalancerIngress) (string, error) { + for _, lb := range ingress { + if ip := lb.IP; len(ip) > 0 { + return ip, nil + } + if hostname := lb.Hostname; len(hostname) > 0 { + return "", fmt.Errorf("hostname not supported for LoadBalancer ingress: use static IP instead") } } diff --git a/internal/kubeadm/certificates.go b/internal/kubeadm/certificates.go index fbb362ec..bdb151b5 100644 --- a/internal/kubeadm/certificates.go +++ b/internal/kubeadm/certificates.go @@ -44,21 +44,32 @@ func GenerateCACertificatePrivateKeyPair(baseName string, config *Configuration) func GenerateCertificatePrivateKeyPair(baseName string, config *Configuration, ca CertificatePrivateKeyPair) (*CertificatePrivateKeyPair, error) { defer deleteCertificateDirectory(config.InitConfiguration.CertificatesDir) - certificate, _ := cryptoKamaji.ParseCertificateBytes(ca.Certificate) - signer, _ := cryptoKamaji.ParsePrivateKeyBytes(ca.PrivateKey) + certificate, err := cryptoKamaji.ParseCertificateBytes(ca.Certificate) + if err != nil { + return nil, fmt.Errorf("failed to parse CA certificate: %w", err) + } + + signer, err := cryptoKamaji.ParsePrivateKeyBytes(ca.PrivateKey) + if err != nil { + return nil, fmt.Errorf("failed to parse CA private key: %w", err) + } kubeadmCert, err := getKubeadmCert(baseName) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get kubeadm cert: %w", err) } if err = initPhaseFromCA(kubeadmCert, config, certificate, signer); err != nil { - return nil, err + return nil, fmt.Errorf("failed to initialize phase from CA: %w", err) } contents, err := readCertificateFiles(baseName, config.InitConfiguration.CertificatesDir, "crt", "key") if err != nil { - return nil, err + return nil, fmt.Errorf("failed to read certificate files: %w", err) + } + + if len(contents) != 2 { + return nil, fmt.Errorf("unexpected number of certificate files: expected 2, got %d", len(contents)) } certificatePrivateKeyPair := &CertificatePrivateKeyPair{ @@ -66,7 +77,7 @@ func GenerateCertificatePrivateKeyPair(baseName string, config *Configuration, c PrivateKey: contents[1], } - return certificatePrivateKeyPair, err + return certificatePrivateKeyPair, nil } func getKubeadmCert(baseName string) (*certs.KubeadmCert, error) { diff --git a/internal/resources/api_server_certificate.go b/internal/resources/api_server_certificate.go index a056dad7..076f50f0 100644 --- a/internal/resources/api_server_certificate.go +++ b/internal/resources/api_server_certificate.go @@ -128,9 +128,9 @@ func (r *APIServerCertificate) mutate(ctx context.Context, tenantControlPlane *k config, err := getStoredKubeadmConfiguration(ctx, r.Client, r.TmpDirectory, tenantControlPlane) if err != nil { - logger.Error(err, "cannot retrieve kubeadm configuration") + logger.Error(err, "cannot generate certificate and private key in api server certificate", "details", err.Error()) - return err + return fmt.Errorf("failed to generate certificate and private key: %w", err) } ca := kubeadm.CertificatePrivateKeyPair{