internal/kubernetes/unauthenticated.go (69 lines of code) (raw):

package kubernetes import ( "context" "crypto/tls" "crypto/x509" "encoding/json" "errors" "fmt" "io" "net/http" "github.com/aws/eks-hybrid/internal/api" "github.com/aws/eks-hybrid/internal/validation" ) func MakeUnauthenticatedRequest(ctx context.Context, endpoint string, caCertificate []byte) error { caCertPool := x509.NewCertPool() if !caCertPool.AppendCertsFromPEM(caCertificate) { return validation.WithRemediation(errors.New("failed to parse Cluster CA certificate"), "Ensure the Cluster CA certificate provided is correct.", ) } client := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: caCertPool, }, }, } req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil) if err != nil { return validation.WithRemediation(err, "Ensure the Kubernetes API server endpoint provided is correct.") } resp, err := client.Do(req) if err != nil { return validation.WithRemediation(err, "Ensure the provided Kubernetes API server endpoint is correct and the CA certificate is valid for that endpoint.") } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("reading unauthenticated request response body: %w", err) } apiServerResp := &apiServerResponse{} if err = json.Unmarshal(body, apiServerResp); err != nil { return fmt.Errorf("unmarshalling unauthenticated request response: %w", err) } // We allow both Forbidden and Unauthorized status codes because the API server will return // The kube-API server used to return Forbidden but in k8s 1.32 it started returning Unauthorized. if resp.StatusCode != http.StatusForbidden && resp.StatusCode != http.StatusUnauthorized { return validation.WithRemediation(fmt.Errorf("expected status code from unauthenticated request %d or %d, got %d. Message: %s", http.StatusForbidden, http.StatusUnauthorized, resp.StatusCode, apiServerResp.Message), "Ensure the Kubernetes API server endpoint provided is correct and the CA certificate is valid for that endpoint.", ) } return nil } type apiServerResponse struct { Status string `json:"status"` Message string `json:"message"` Reason string `json:"reason"` Code int64 `json:"code"` } func CheckUnauthenticatedAccess(ctx context.Context, informer validation.Informer, node *api.NodeConfig) error { name := "kubernetes-unauthenticated-request" var err error informer.Starting(ctx, name, "Validating unauthenticated request to Kubernetes API endpoint") defer func() { informer.Done(ctx, name, err) }() if err = MakeUnauthenticatedRequest(ctx, node.Spec.Cluster.APIServerEndpoint, node.Spec.Cluster.CertificateAuthority); err != nil { return err } return nil }