Skip to content

Commit 1ef7c0e

Browse files
committed
Use instance domain
1 parent 99ffa41 commit 1ef7c0e

File tree

2 files changed

+60
-16
lines changed

2 files changed

+60
-16
lines changed

cmd/ssh.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,12 @@ func connectSSH(ctx context.Context, client kernel.Client, cfg ssh.Config) error
8585
return fmt.Errorf("failed to get browser: %w", err)
8686
}
8787

88-
// Extract VM domain from live view URL or CDP URL
88+
// Extract VM domain from CDP URL (which contains the JWT with the actual FQDN)
8989
var vmDomain string
90-
if browser.BrowserLiveViewURL != "" {
91-
vmDomain, err = ssh.ExtractVMDomain(browser.BrowserLiveViewURL)
92-
} else if browser.CdpWsURL != "" {
90+
if browser.CdpWsURL != "" {
9391
vmDomain, err = ssh.ExtractVMDomain(browser.CdpWsURL)
9492
} else {
95-
return fmt.Errorf("browser has no live view URL or CDP URL - cannot determine VM domain")
93+
return fmt.Errorf("browser has no CDP URL - cannot determine VM domain")
9694
}
9795
if err != nil {
9896
return fmt.Errorf("failed to extract VM domain: %w", err)

pkg/ssh/ssh.go

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ package ssh
44
import (
55
"crypto/ed25519"
66
"crypto/rand"
7+
"encoding/base64"
8+
"encoding/json"
79
"encoding/pem"
810
"fmt"
911
"net/url"
@@ -60,26 +62,70 @@ func GenerateKeyPair() (*KeyPair, error) {
6062
}, nil
6163
}
6264

63-
// ExtractVMDomain extracts the VM hostname from a BrowserLiveViewURL or CdpWsURL.
64-
// Examples:
65-
// - "https://vm-abc123.kernel.live/..." -> "vm-abc123.kernel.live"
66-
// - "wss://vm-abc123.kernel.live/..." -> "vm-abc123.kernel.live"
67-
func ExtractVMDomain(rawURL string) (string, error) {
68-
if rawURL == "" {
65+
// ExtractVMDomain extracts the VM FQDN from a CDP WebSocket URL by decoding the JWT.
66+
// The CDP URL contains a JWT with the actual Unikraft FQDN in the payload.
67+
// Example CDP URL: wss://proxy.xxx.dev.onkernel.com:8443/browser/cdp?jwt=eyJ...
68+
// The JWT payload contains: {"session": {"fqdn": "actual-vm-domain.onkernel.app"}}
69+
func ExtractVMDomain(cdpURL string) (string, error) {
70+
if cdpURL == "" {
6971
return "", fmt.Errorf("empty URL")
7072
}
7173

72-
parsed, err := url.Parse(rawURL)
74+
parsed, err := url.Parse(cdpURL)
7375
if err != nil {
7476
return "", fmt.Errorf("failed to parse URL: %w", err)
7577
}
7678

77-
host := parsed.Hostname()
78-
if host == "" {
79-
return "", fmt.Errorf("no hostname in URL: %s", rawURL)
79+
// Extract JWT from query parameter
80+
jwt := parsed.Query().Get("jwt")
81+
if jwt == "" {
82+
// Fallback to hostname if no JWT (shouldn't happen in practice)
83+
host := parsed.Hostname()
84+
if host == "" {
85+
return "", fmt.Errorf("no hostname in URL: %s", cdpURL)
86+
}
87+
return host, nil
8088
}
8189

82-
return host, nil
90+
// JWT is header.payload.signature - we need the payload (middle part)
91+
parts := strings.Split(jwt, ".")
92+
if len(parts) != 3 {
93+
return "", fmt.Errorf("invalid JWT format")
94+
}
95+
96+
// Decode base64url payload
97+
payload := parts[1]
98+
// Add padding if needed (base64url may omit padding)
99+
switch len(payload) % 4 {
100+
case 2:
101+
payload += "=="
102+
case 3:
103+
payload += "="
104+
}
105+
// Convert base64url to standard base64
106+
payload = strings.ReplaceAll(payload, "-", "+")
107+
payload = strings.ReplaceAll(payload, "_", "/")
108+
109+
decoded, err := base64.StdEncoding.DecodeString(payload)
110+
if err != nil {
111+
return "", fmt.Errorf("failed to decode JWT payload: %w", err)
112+
}
113+
114+
// Parse JSON payload
115+
var claims struct {
116+
Session struct {
117+
FQDN string `json:"fqdn"`
118+
} `json:"session"`
119+
}
120+
if err := json.Unmarshal(decoded, &claims); err != nil {
121+
return "", fmt.Errorf("failed to parse JWT payload: %w", err)
122+
}
123+
124+
if claims.Session.FQDN == "" {
125+
return "", fmt.Errorf("no FQDN in JWT payload")
126+
}
127+
128+
return claims.Session.FQDN, nil
83129
}
84130

85131
// CheckWebsocatInstalled verifies websocat is available in PATH.

0 commit comments

Comments
 (0)