Skip to content

Add custom API server endpoint configuration for proxy scenarios#4635

Draft
Copilot wants to merge 22 commits intomainfrom
copilot/allow-custom-api-server-endpoint
Draft

Add custom API server endpoint configuration for proxy scenarios#4635
Copilot wants to merge 22 commits intomainfrom
copilot/allow-custom-api-server-endpoint

Conversation

Copy link
Contributor

Copilot AI commented Feb 7, 2026

Summary

This PR adds custom API server endpoint configuration for in-cluster deployments by introducing a new --api-server-endpoint flag, environment variable, and Helm chart value. This enables routing Kubernetes API requests through proxy servers like kube-oidc-proxy, which is particularly useful when OIDC issuers are on private endpoints (common with managed Kubernetes services like AWS EKS, Azure AKS, and Google GKE).

Changes

  • Added APIServerEndpoint field to backend config with --api-server-endpoint CLI flag and HEADLAMP_CONFIG_API_SERVER_ENDPOINT environment variable support
  • Added comprehensive URL validation with security hardening:
    • Enforces https:// scheme only
    • Validates absolute URLs with non-empty hostname
    • Validates port is numeric and in valid range (1-65535)
    • Rejects embedded credentials, query strings, fragments, and non-root paths
    • Completely redacts user input from error messages to prevent secret leakage
    • Validates before loading in-cluster config for consistent error messages
  • Updated GetInClusterContext() to accept and use custom endpoint when provided
  • Extracted helper functions (validateAPIServerEndpoint, buildOIDCConfig, setupInClusterContext) for code quality and maintainability
  • Fixed critical nil pointer bug in headlamp.go that could crash server on validation errors
  • Added Helm chart support:
    • Added config.apiServerEndpoint value to values.yaml
    • Updated values.schema.json for schema validation
    • Updated deployment template with proper quoting to handle special characters
    • Added Helm chart test case
  • Added comprehensive test coverage:
    • 18 unit tests for URL validation (6 valid cases + 6 format errors + 6 security/port errors)
    • Split into ValidCases, InvalidCases_Format, and InvalidCases_Security test functions (all < 60 lines)
    • Integration test that validates https enforcement
    • Config parsing tests for flag and environment variable
  • Added documentation:
    • Created dedicated docs/installation/in-cluster/custom-api-endpoint.md file with 104 lines covering architecture, use cases, benefits, and manual testing guide
    • Updated docs/installation/in-cluster/index.md with brief intro and link
    • Updated Helm chart README with configuration table entry and examples
    • Updated all kube-oidc-proxy references to new repository (TremoloSecurity/kube-oidc-proxy)
    • Included examples for AWS EKS, Azure AKS, and Google GKE

Steps to Test

  1. Deploy with Helm: helm install headlamp headlamp/headlamp --set config.apiServerEndpoint=https://proxy.example.com:443
  2. Verify pod args contain -api-server-endpoint=https://proxy.example.com:443: kubectl get pod -n kube-system -l app.kubernetes.io/name=headlamp -o jsonpath='{.items[0].spec.containers[0].args}'
  3. Confirm Headlamp connects through specified endpoint (check logs for API server host)
  4. Test backward compatibility: deploy without setting apiServerEndpoint, verify default in-cluster endpoint detection works
  5. Run Helm template tests: make helm-template-test - all 25 tests should pass including the new api-server-endpoint test case
  6. Test validation: Try setting invalid endpoints and verify clear error messages without exposing secrets:
    • http://insecure.com - rejects non-https
    • https://user:pass@host.com - rejects credentials
    • https://proxy.com:abc - rejects non-numeric port
    • https://proxy.com:99999 - rejects out-of-range port
  7. Run backend linter: npm run backend:lint - should pass with no errors
  8. Run backend tests: npm run backend:test - all tests should pass
  9. For manual testing with kube-oidc-proxy, follow the detailed step-by-step guide in docs/installation/in-cluster/custom-api-endpoint.md

Notes for the Reviewer

  • Change is additive only—empty value preserves existing behavior
  • No changes to authentication/authorization logic, purely endpoint routing configuration
  • All error messages completely redact user input to prevent logging credentials, tokens, or other secrets
  • Code quality maintained: all functions under 60-line limit, nesting complexity reduced, linter passes with no errors
  • Comprehensive test coverage with 18 unit tests (split into 3 test functions), integration test, and Helm template tests
  • All Helm chart files (values.yaml, values.schema.json, README.md) are kept in sync
  • Documentation includes architecture explanations, benefits, and manual testing guide for kube-oidc-proxy
  • Fixed critical nil pointer bug that could crash server on misconfiguration
  • Port validation prevents runtime dial errors with clear, early error messages

Fixes #2835

Original prompt

This section details on the original issue you should resolve

<issue_title>Allow Custom API-Server Endpoint</issue_title>
<issue_description>For some use-case, especially on public cloud, some request have to pass through some proxy for handling. E.g. kube-oidc-server if OIDC issuer is from private endpoint. A flag and helm chart value can be also added for this item.
https://aws.amazon.com/blogs/opensource/consistent-oidc-authentication-across-multiple-eks-clusters-using-kube-oidc-proxy/</issue_description>

Comments on the Issue (you are @copilot in this section)

@illume Hello @Jaguar-Kwok

could you please expand a little bit on what you need from Headlamp for this? To help my understanding.

It sounds like you would like:

  • a configurable API server end point (only one or multiple?)
  • A way for the helm chart to pass this configuration onto the headlamp image
  • So that you could use Kube-OIDC-Proxy with EKS.

We already reverse proxy api endpoints with the headlamp server, so I’m wondering if this feature might be best or easily added directly to Headlamp. Since we want to support managed Kubernetes providers better in Headlamp, I wonder if this will be a very common need?

@illume /lifecycle frozen

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. labels Feb 7, 2026
@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Feb 7, 2026
- Added APIServerEndpoint field to backend config
- Added --api-server-endpoint CLI flag and environment variable support
- Updated GetInClusterContext to accept and use custom endpoint
- Added Helm chart value config.apiServerEndpoint
- Updated deployment template to pass value as argument

Co-authored-by: illume <9541+illume@users.noreply.github.com>
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: Copilot
Once this PR has been reviewed and has the lgtm label, please ask for approval from illume. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added size/S Denotes a PR that changes 10-29 lines, ignoring generated files. and removed size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. labels Feb 7, 2026
- Added test for --api-server-endpoint flag
- Added test for HEADLAMP_CONFIG_API_SERVER_ENDPOINT env var
- Verified Helm template renders correctly with apiServerEndpoint value

Co-authored-by: illume <9541+illume@users.noreply.github.com>
@k8s-ci-robot k8s-ci-robot added size/M Denotes a PR that changes 30-99 lines, ignoring generated files. and removed size/S Denotes a PR that changes 10-29 lines, ignoring generated files. labels Feb 7, 2026
- Updated Helm chart README with apiServerEndpoint configuration
- Added installation example with custom API endpoint
- Documented use case with kube-oidc-proxy

Co-authored-by: illume <9541+illume@users.noreply.github.com>
Co-authored-by: illume <9541+illume@users.noreply.github.com>
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Feb 7, 2026
Copilot AI changed the title [WIP] Add configurable API server endpoint for Headlamp Add custom API server endpoint configuration for proxy scenarios Feb 7, 2026
Copilot AI requested a review from illume February 7, 2026 19:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a configurable Kubernetes API server endpoint override for Headlamp’s in-cluster mode, enabling setups where API traffic must go through an external/proxy endpoint (e.g., kube-oidc-proxy).

Changes:

  • Introduces api-server-endpoint configuration via backend config struct, CLI flag, and HEADLAMP_CONFIG_API_SERVER_ENDPOINT env var (with unit tests).
  • Updates in-cluster context creation to optionally override the cluster API server Host.
  • Adds Helm chart value + deployment arg wiring and documents usage in the chart README.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
charts/headlamp/values.yaml Adds config.apiServerEndpoint chart value with documentation comments.
charts/headlamp/templates/deployment.yaml Passes -api-server-endpoint=... arg when configured.
charts/headlamp/README.md Documents Helm installation example and adds config table entry.
backend/pkg/kubeconfig/kubeconfig.go Extends GetInClusterContext to accept and apply a custom API server endpoint.
backend/pkg/headlampconfig/headlampConfig.go Adds APIServerEndpoint field to backend runtime config struct.
backend/pkg/config/config_test.go Adds tests for flag/env parsing of the new setting.
backend/pkg/config/config.go Adds config field and CLI flag for api-server-endpoint.
backend/cmd/server.go Maps parsed config into HeadlampCFG including the new endpoint field.
backend/cmd/headlamp.go Passes endpoint value into GetInClusterContext.
Files not reviewed (1)
  • frontend/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@illume
Copy link
Contributor

illume commented Feb 7, 2026

@copilot address all open review comments

- Added Helm chart test case for apiServerEndpoint configuration
- Generated expected template for the test case
- Added comprehensive documentation in docs/installation/in-cluster/index.md
- Reverted unneeded frontend/package-lock.json changes
- All Helm template tests pass

Co-authored-by: illume <9541+illume@users.noreply.github.com>
@illume
Copy link
Contributor

illume commented Feb 8, 2026

@copilot fix lint error

Error: backend/pkg/kubeconfig/kubeconfig_test.go:830:6: Function 'TestValidateAPIServerEndpoint' is too long (109 > 60) (funlen)
func TestValidateAPIServerEndpoint(t *testing.T) {
^
Error: backend/cmd/headlamp.go:430:1: if config.UseInCluster has complex nested blocks (complexity: 6) (nestif)
if config.UseInCluster {
^

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Split TestValidateAPIServerEndpoint into two functions:
  - TestValidateAPIServerEndpoint_ValidCases (37 lines)
  - TestValidateAPIServerEndpoint_InvalidCases (40 lines)
- Extract setupInClusterContext helper to reduce nesting complexity
- Reduces if config.UseInCluster block from complexity 6 to 1
- All tests still pass (14 validation tests + integration test)
- Backend linter passes with no errors

Addresses lint errors: funlen and nestif.

Co-authored-by: illume <9541+illume@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 8, 2026

@copilot fix lint error

Error: backend/pkg/kubeconfig/kubeconfig_test.go:830:6: Function 'TestValidateAPIServerEndpoint' is too long (109 > 60) (funlen)
...

Fixed both lint errors:

  • Split TestValidateAPIServerEndpoint into ValidCases (37 lines) and InvalidCases (40 lines) - both under 60-line limit
  • Extracted setupInClusterContext helper function to reduce nesting complexity from 6 to 1
  • Backend linter now passes with no errors
  • All tests still pass (14 validation + integration + config tests)

(738c6e0)

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • example.com
    • Triggering command: /tmp/go-build2630811747/b1352/serviceproxy.test /tmp/go-build2630811747/b1352/serviceproxy.test -test.testlogfile=/tmp/go-build2630811747/b1352/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true rg/x/text@v0.22.-errorsas rg/x/text@v0.22.-ifaceassert nux-amd64/pkg/tool/linux_amd64/vet -p github.com/golan-atomic -lang=go1.23 nux-amd64/pkg/to-buildtags 7097�� 7097330/b1046/_p-errorsas -trimpath nux-amd64/pkg/tool/linux_amd64/vet -p t/message/catalo--64 -lang=go1.22 nux-amd64/pkg/to/tmp/ccvmxFBF.o (dns block)
  • kubernetes-sigs.github.io
    • Triggering command: /tmp/go-build2630811747/b1270/helm.test /tmp/go-build2630811747/b1270/helm.test -test.testlogfile=/tmp/go-build2630811747/b1270/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true kg_.a -trimpath nux-amd64/pkg/tool/linux_amd64/vet -p d/autoscaling/v2-atomic -lang=go1.23 nux-amd64/pkg/to-buildtags -o tMtib-_ON -trimpath nux-amd64/pkg/tool/linux_amd64/vet -p github.com/tommy-atomic -lang=go1.12 nux-amd64/pkg/to-buildtags (dns block)
    • Triggering command: /tmp/go-build1393228814/b1270/helm.test /tmp/go-build1393228814/b1270/helm.test -test.testlogfile=/tmp/go-build1393228814/b1270/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true u/13/liblto_plugin.so _64-linux-gnu/13/lto-wrapper 0.1-go1.24.13.linux-amd64/pkg/tool/linux_amd64/vet -p s ompile 0.1-go1.24.13.li-buildtags s 9322074/b1338/_p-errorsas ompile nux-amd64/pkg/tool/linux_amd64/vet . netes-sigs/headl--64 --64 nux-amd64/pkg/to/tmp/ccEwzhJE.o (dns block)
  • test-cluster.example.com
    • Triggering command: /tmp/go-build2630811747/b001/cmd.test /tmp/go-build2630811747/b001/cmd.test -test.testlogfile=/tmp/go-build2630811747/b001/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true /kubernetes/type-errorsas /kubernetes/type-ifaceassert nux-amd64/pkg/tool/linux_amd64/vet nternal/xtypes/x/home/REDACTED/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.13.linux-amd64/pkg/to-buildtags ls/staticcheck/s-atomic -lang=go1.23 nux-amd64/pkg/to-buildtags -o kg_.a -trimpath nux-amd64/pkg/tool/linux_amd64/vet -p d/coordination/v-atomic -lang=go1.23 nux-amd64/pkg/to-buildtags (dns block)
    • Triggering command: /tmp/go-build1393228814/b001/cmd.test /tmp/go-build1393228814/b001/cmd.test -test.testlogfile=/tmp/go-build1393228814/b001/testlog.txt -test.paniconexit0 -test.timeout=10m0s -test.v=true amp/backend/pkg/-errorsas -trimpath 0.1-go1.24.13.li-nilfunc -p honnef.co/go/too--64 -lang=go1.23 0.1-go1.24.13.li/tmp/ccJHyQvJ.o -o ... -trimpath nux-amd64/pkg/tool/linux_amd64/vet -p nt.go ompile nux-amd64/pkg/tool/linux_amd64/vet (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@illume
Copy link
Contributor

illume commented Feb 8, 2026

@copilot please address open review comments?

- Add config.apiServerEndpoint to charts/headlamp/values.schema.json
- Redact user-supplied endpoints from error messages to prevent secret logging
- For credentials/query/fragment errors: omit endpoint completely
- For scheme errors: include only scheme+host (safe parts)
- For path errors: include scheme+host+path (safe parts)
- All 14 validation tests still pass
- JSON schema valid

Addresses security concern about logging secrets in error messages.

Co-authored-by: illume <9541+illume@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Feb 8, 2026

@copilot please address open review comments?

All open review comments addressed:

  • ✅ values.schema.json updated (2779185250)
  • ✅ Secrets redacted from error messages (2779185252)
  • ✅ Backend linter passes (0 errors)
  • ✅ Backend builds successfully
  • ✅ All tests pass (14 validation + integration + config tests)
  • ✅ JSON schema valid

(8d7a767)

- Remove scheme+host from non-https error message
- Remove path from path validation error message
- All error messages now completely redact user input
- Prevents any potential information disclosure
- All 14 validation tests still pass

Final security hardening per code review feedback.

Co-authored-by: illume <9541+illume@users.noreply.github.com>
- Update all references from jetstack/kube-oidc-proxy to TremoloSecurity/kube-oidc-proxy
- Repository has moved to new maintainer
- Updated in custom-api-endpoint.md (2 occurrences)
- Updated in charts/headlamp/README.md (1 occurrence)

Addresses comment 2779229494.

Co-authored-by: illume <9541+illume@users.noreply.github.com>
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1012 to +1018
parsedURL, err := url.Parse(trimmed)
if err != nil || !parsedURL.IsAbs() || parsedURL.Host == "" || parsedURL.Hostname() == "" {
// Don't include the endpoint in error as it may contain sensitive data
return "", fmt.Errorf(
"invalid custom API server endpoint: must be an absolute URL with scheme and host",
)
}
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

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

validateAPIServerEndpoint currently accepts endpoints with an invalid/non-numeric port (e.g. https://proxy.example.com:abc) because url.Parse does not validate the port component. That value would pass validation but later fail when the proxy/client tries to dial, producing a harder-to-diagnose runtime error. Consider validating parsedURL.Port() when present (parse to int and ensure 1-65535) and add a unit test for an invalid port case.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot make it so

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added port validation to check Port() when present and ensure it's a valid number between 1-65535. Added 4 test cases covering non-numeric ports (caught by url.Parse error), port 0, port 99999, and negative ports. Tests split into Format and Security functions to stay under 60-line limit. All 18 validation tests pass. (2b0d627)

Copilot AI and others added 2 commits February 8, 2026 14:18
- Validate port is numeric and in range 1-65535
- Add strconv import for port parsing
- Add 4 test cases for port validation:
  - Non-numeric port (abc) - caught by url.Parse
  - Port too high (99999) - caught by port validation
  - Port zero (0) - caught by port validation
  - Negative port (-1) - caught by url.Parse
- Split InvalidCases tests into Format and Security functions (both < 60 lines)
- All 18 validation tests pass
- Prevents hard-to-diagnose runtime errors when dialing invalid ports

Addresses review comment 2779302441.

Co-authored-by: illume <9541+illume@users.noreply.github.com>
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 14, 2026
@k8s-ci-robot
Copy link
Contributor

PR needs rebase.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow Custom API-Server Endpoint

3 participants