Improve bootstrap token UX with smart environment detection
- Added DetectLXCCTID() to internal/system/container.go to detect Proxmox container ID - Extended /api/security/status to expose inContainer and lxcCtid fields - Updated FirstRunSetup to show most relevant command based on detected environment: * LXC with CTID: Shows 'pct exec 171 -- cat /etc/pulse/.bootstrap_token' * Docker: Shows 'docker exec <container-name> cat /data/.bootstrap_token' * Bare metal: Shows 'cat /etc/pulse/.bootstrap_token' - Collapsed alternative methods behind 'Show other retrieval methods' button This addresses user feedback that showing all options was overwhelming. Now users see the command most likely to work for their setup first, with alternatives hidden but still accessible.
This commit is contained in:
parent
dd9089b26c
commit
ae0481c982
5 changed files with 131 additions and 16 deletions
8
copy_and_run.sh
Executable file
8
copy_and_run.sh
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
cd /
|
||||
if [ -d /tmp/pulse-copy ]; then
|
||||
rm -rf /tmp/pulse-copy
|
||||
fi
|
||||
mkdir -p /tmp/pulse-copy
|
||||
cp -R /opt/pulse /tmp/pulse-copy/repo
|
||||
cd /tmp/pulse-copy/repo
|
||||
GOCACHE=/tmp/go-cache HOME=/tmp/pulse-copy/repo go test ./...
|
||||
|
|
@ -26,6 +26,9 @@ export const FirstRunSetup: Component<{ force?: boolean; showLegacyBanner?: bool
|
|||
const [isUnlocked, setIsUnlocked] = createSignal(false);
|
||||
const [bootstrapTokenPath, setBootstrapTokenPath] = createSignal<string>('');
|
||||
const [isDocker, setIsDocker] = createSignal<boolean>(false);
|
||||
const [inContainer, setInContainer] = createSignal<boolean>(false);
|
||||
const [lxcCtid, setLxcCtid] = createSignal<string>('');
|
||||
const [showAlternatives, setShowAlternatives] = createSignal(false);
|
||||
|
||||
const applyTheme = (mode: 'system' | 'light' | 'dark') => {
|
||||
if (mode === 'light') {
|
||||
|
|
@ -72,6 +75,8 @@ export const FirstRunSetup: Component<{ force?: boolean; showLegacyBanner?: bool
|
|||
if (data.bootstrapTokenPath) {
|
||||
setBootstrapTokenPath(data.bootstrapTokenPath);
|
||||
setIsDocker(data.isDocker || false);
|
||||
setInContainer(data.inContainer || false);
|
||||
setLxcCtid(data.lxcCtid || '');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -317,26 +322,68 @@ IMPORTANT: Keep these credentials secure!
|
|||
</div>
|
||||
}
|
||||
>
|
||||
{/* Show most relevant command based on detected environment */}
|
||||
<div class="bg-white dark:bg-gray-800 rounded p-3 font-mono text-xs text-gray-800 dark:text-gray-200">
|
||||
<Show when={isDocker()}>
|
||||
<div class="text-blue-600 dark:text-blue-400 mb-1"># From Docker host:</div>
|
||||
docker exec <container-name> cat {bootstrapTokenPath()}
|
||||
<div class="text-gray-500 dark:text-gray-400 mt-2 mb-1"># For Kubernetes:</div>
|
||||
kubectl exec <pod-name> -- cat {bootstrapTokenPath()}
|
||||
<div class="text-gray-500 dark:text-gray-400 mt-2 mb-1"># For Proxmox LXC running Docker:</div>
|
||||
pct exec <ctid> -- docker exec <container-name> cat {bootstrapTokenPath()}
|
||||
<div class="text-gray-500 dark:text-gray-400 mt-2 mb-1"># Or enter the LXC container first:</div>
|
||||
pct enter <ctid>
|
||||
<div class="text-gray-500 dark:text-gray-400 mt-1 mb-1"># Then inside LXC:</div>
|
||||
{/* LXC with detected CTID */}
|
||||
<Show when={inContainer() && !isDocker() && lxcCtid()}>
|
||||
<div class="text-blue-600 dark:text-blue-400 mb-1 font-semibold"># Run from Proxmox host:</div>
|
||||
pct exec {lxcCtid()} -- cat {bootstrapTokenPath()}
|
||||
</Show>
|
||||
<Show when={!isDocker() && bootstrapTokenPath() === '/etc/pulse/.bootstrap_token'}>
|
||||
<div class="text-blue-600 dark:text-blue-400 mb-1"># For Proxmox LXC, run from Proxmox host:</div>
|
||||
pct enter <ctid>
|
||||
<div class="text-gray-500 dark:text-gray-400 mt-2 mb-1"># Or directly retrieve:</div>
|
||||
|
||||
{/* LXC without CTID */}
|
||||
<Show when={inContainer() && !isDocker() && !lxcCtid()}>
|
||||
<div class="text-blue-600 dark:text-blue-400 mb-1 font-semibold"># Run from Proxmox host:</div>
|
||||
pct exec <ctid> -- cat {bootstrapTokenPath()}
|
||||
<div class="text-gray-500 dark:text-gray-400 mt-2 mb-1"># Or from inside the LXC container:</div>
|
||||
</Show>
|
||||
cat {bootstrapTokenPath()}
|
||||
|
||||
{/* Docker (generic) */}
|
||||
<Show when={isDocker()}>
|
||||
<div class="text-blue-600 dark:text-blue-400 mb-1 font-semibold"># From Docker host:</div>
|
||||
docker exec <container-name> cat {bootstrapTokenPath()}
|
||||
</Show>
|
||||
|
||||
{/* Bare metal / inside container */}
|
||||
<Show when={!inContainer()}>
|
||||
<div class="text-blue-600 dark:text-blue-400 mb-1 font-semibold"># On this host:</div>
|
||||
cat {bootstrapTokenPath()}
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
{/* Collapsible alternatives */}
|
||||
<div class="mt-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowAlternatives(!showAlternatives())}
|
||||
class="text-xs text-blue-600 dark:text-blue-400 hover:underline"
|
||||
>
|
||||
{showAlternatives() ? '▼' : '▶'} Show other retrieval methods
|
||||
</button>
|
||||
<Show when={showAlternatives()}>
|
||||
<div class="mt-2 space-y-2 text-gray-600 dark:text-gray-400">
|
||||
<Show when={isDocker()}>
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded p-3 font-mono text-xs">
|
||||
<div class="mb-1"># For Kubernetes:</div>
|
||||
kubectl exec <pod-name> -- cat {bootstrapTokenPath()}
|
||||
</div>
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded p-3 font-mono text-xs">
|
||||
<div class="mb-1"># For Proxmox LXC running Docker:</div>
|
||||
pct exec <ctid> -- docker exec <container-name> cat {bootstrapTokenPath()}
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={inContainer() && !isDocker()}>
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded p-3 font-mono text-xs">
|
||||
<div class="mb-1"># Enter container then run:</div>
|
||||
pct enter {lxcCtid() || '<ctid>'}
|
||||
<br />
|
||||
cat {bootstrapTokenPath()}
|
||||
</div>
|
||||
</Show>
|
||||
<div class="bg-gray-50 dark:bg-gray-900 rounded p-3 font-mono text-xs">
|
||||
<div class="mb-1"># Direct file access:</div>
|
||||
cat {bootstrapTokenPath()}
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/rcourtman/pulse-go-rewrite/internal/dockeragent"
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/models"
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/monitoring"
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/system"
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/tempproxy"
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/updates"
|
||||
"github.com/rcourtman/pulse-go-rewrite/internal/utils"
|
||||
|
|
@ -485,6 +486,10 @@ func (r *Router) setupRoutes() {
|
|||
if r.bootstrapTokenHash != "" {
|
||||
status["bootstrapTokenPath"] = r.bootstrapTokenPath
|
||||
status["isDocker"] = os.Getenv("PULSE_DOCKER") == "true"
|
||||
status["inContainer"] = system.InContainer()
|
||||
if ctid := system.DetectLXCCTID(); ctid != "" {
|
||||
status["lxcCtid"] = ctid
|
||||
}
|
||||
}
|
||||
|
||||
if r.config.DisableAuthEnvDetected {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,58 @@ func InContainer() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// DetectLXCCTID attempts to detect the Proxmox LXC container ID.
|
||||
// Returns empty string if not in an LXC container or CTID cannot be determined.
|
||||
func DetectLXCCTID() string {
|
||||
// Method 1: Parse /proc/1/cgroup for LXC container ID
|
||||
if data, err := os.ReadFile("/proc/1/cgroup"); err == nil {
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for _, line := range lines {
|
||||
// Look for patterns like: 0::/lxc/123 or 0::/lxc.payload.123
|
||||
if strings.Contains(line, "/lxc") || strings.Contains(line, "lxc.payload") {
|
||||
// Extract digits after lxc/ or lxc.payload.
|
||||
parts := strings.Split(line, "/")
|
||||
for _, part := range parts {
|
||||
part = strings.TrimPrefix(part, "lxc.payload.")
|
||||
part = strings.TrimPrefix(part, "lxc.")
|
||||
if isNumeric(part) {
|
||||
return part
|
||||
}
|
||||
}
|
||||
}
|
||||
// Also check for machine-lxc-NNN pattern
|
||||
if strings.Contains(line, "machine-lxc") {
|
||||
fields := strings.Split(line, "-")
|
||||
for _, field := range fields {
|
||||
if isNumeric(field) {
|
||||
return field
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method 2: Check hostname (some LXC containers use CTID as hostname)
|
||||
if hostname, err := os.Hostname(); err == nil && isNumeric(hostname) {
|
||||
return hostname
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func isNumeric(s string) bool {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
for _, c := range s {
|
||||
if c < '0' || c > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isTruthy(value string) bool {
|
||||
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||
case "1", "true", "t", "yes", "y", "on":
|
||||
|
|
|
|||
3
work.sh
Normal file
3
work.sh
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
cd /opt/pulse
|
||||
git checkout cleanup-auto-uninstall
|
||||
Loading…
Reference in a new issue