cli: authenticate Hub HTTP requests with License-Key (gated-auth phase 1)

Add a License-Key RoundTripper plus NewHubHTTPClient / HubAuthTransport
helpers in utils, and route the MCP runner's Hub clients (connect, --url
validate, file download, tools list) through them so the CLI keeps working
once the Hub enforces auth. License-Key is a custom header, so it survives
the kube API-server service proxy (a bearer token would not). Surface
ErrHubAuthRequired on a 401/302 from the Hub.

console already sends License-Key on its ws connection. pprof is not
covered here: it shells out to 'go tool pprof <hubUrl>' / opens a browser,
external fetches that can't carry a custom header (follow-up).
This commit is contained in:
Volodymyr Stoiko
2026-06-15 20:59:27 +00:00
parent 9396e64b9b
commit 6ba67ef3f5
2 changed files with 60 additions and 5 deletions

View File

@@ -20,6 +20,7 @@ import (
"github.com/kubeshark/kubeshark/internal/connect"
"github.com/kubeshark/kubeshark/kubernetes"
"github.com/kubeshark/kubeshark/misc"
"github.com/kubeshark/kubeshark/utils"
"github.com/rs/zerolog"
)
@@ -167,7 +168,7 @@ func runMCPWithConfig(setFlags []string, directURL string, allowDestructive bool
zerolog.SetGlobalLevel(zerolog.Disabled)
server := &mcpServer{
httpClient: &http.Client{Timeout: 30 * time.Second},
httpClient: utils.NewHubHTTPClient(30*time.Second, config.Config.License),
stdin: os.Stdin,
stdout: os.Stdout,
setFlags: setFlags,
@@ -195,7 +196,7 @@ func (s *mcpServer) validateDirectURL() error {
s.directURL = urlStr
// Use a short timeout for validation
client := &http.Client{Timeout: 10 * time.Second}
client := utils.NewHubHTTPClient(10*time.Second, config.Config.License)
// Try to reach the MCP API base endpoint which returns tool definitions
testURL := fmt.Sprintf("%s/api/mcp", urlStr)
@@ -205,6 +206,10 @@ func (s *mcpServer) validateDirectURL() error {
}
defer func() { _ = resp.Body.Close() }()
if utils.IsAuthRequired(resp) {
return utils.ErrHubAuthRequired
}
// Try to parse the MCP response to validate it's a valid Kubeshark endpoint
var mcpInfo hubMCPResponse
if err := json.NewDecoder(resp.Body).Decode(&mcpInfo); err != nil {
@@ -820,10 +825,10 @@ func (s *mcpServer) callDownloadFile(args map[string]any) (string, bool) {
// The default s.httpClient has a 30s total timeout which would fail for large files (up to 10GB).
// This client sets only connection-level timeouts and lets the body stream without a deadline.
downloadClient := &http.Client{
Transport: &http.Transport{
Transport: utils.HubAuthTransport(config.Config.License, &http.Transport{
TLSHandshakeTimeout: 10 * time.Second,
ResponseHeaderTimeout: 30 * time.Second,
},
}),
}
resp, err := downloadClient.Get(fullURL)
@@ -1145,7 +1150,7 @@ func establishProxyConnection(timeout time.Duration) (string, error) {
// fetchAndDisplayTools fetches tools from the Kubeshark API and displays them
func fetchAndDisplayTools(hubURL string, timeout time.Duration) {
client := &http.Client{Timeout: timeout}
client := utils.NewHubHTTPClient(timeout, config.Config.License)
// Fetch tools list from /api/mcp endpoint
resp, err := client.Get(strings.TrimSuffix(hubURL, "/mcp") + "/mcp")