GL-7 Part 1: passphrase hardening on the customer page (security)
The Setup Command panel rendered the per-customer retrieval passphrase (the secret that fetches the WHOLE config) in cleartext twice — as #retrieval-pw text and baked into the Option-3 curl's X-Retrieval-Password header — which contradicts the panel's own "never on the command line" guidance. Now: the retrieval password is MASKED by default (bullet run) with Reveal/Hide + copy-secret controls (value lives in data-secret — the existing reveal model); the Option-3 debug command carries a <YOUR-RETRIEVAL-PASSWORD> placeholder, never the secret. Render test asserts the secret is not baked into any command + is masked by default; red-proof (bake it back) FAILS. (A zero-secret-in-DOM reveal-on-demand fetch is a noted follow-up, not this task.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -360,9 +360,11 @@
|
||||
<div>
|
||||
<span class="label">Retrieval Password</span>
|
||||
<div class="credential-box">
|
||||
<code id="retrieval-pw">{{.Config.RetrievalPassword}}</code>
|
||||
<button type="button" class="copy-btn" onclick="copyText('retrieval-pw')" title="Copy">⎘</button>
|
||||
<code id="retrieval-pw" data-secret="{{.Config.RetrievalPassword}}">••••••••••••••••</code>
|
||||
<button type="button" class="copy-btn" id="reveal-retrieval-pw" onclick="toggleSecret('retrieval-pw')" title="Reveal / hide">Reveal</button>
|
||||
<button type="button" class="copy-btn" onclick="copySecret('retrieval-pw')" title="Copy">⎘</button>
|
||||
</div>
|
||||
<span class="form-hint">The per-customer secret that fetches the whole config — masked by default; never place it on a command line (the installer reads it at a no-echo prompt).</span>
|
||||
</div>
|
||||
<form method="POST" action="/configs/{{.CustomerID}}/regen-password" style="margin-top: 0.5rem;"
|
||||
onsubmit="return confirm('Regenerate retrieval password? The old password will stop working immediately.')">
|
||||
@@ -410,9 +412,9 @@
|
||||
</div>
|
||||
|
||||
<h3 style="margin-top: 1rem;">Option 3: Manual config fetch (debug only)</h3>
|
||||
<p class="text-muted" style="margin: 0 0 0.4rem; font-size: 0.8rem;">The same payload the controller pulls itself — for inspection, not normal provisioning.</p>
|
||||
<p class="text-muted" style="margin: 0 0 0.4rem; font-size: 0.8rem;">The same payload the controller pulls itself — for inspection, not normal provisioning. Replace the placeholder with the Retrieval Password above (Reveal to see it) — it is intentionally NOT baked into this command.</p>
|
||||
<div class="credential-box">
|
||||
<code id="cmd-curl">curl -fsSL https://hub.felhom.eu/api/v1/config/{{.CustomerID}} -H "X-Retrieval-Password: {{.Config.RetrievalPassword}}" -o controller.yaml</code>
|
||||
<code id="cmd-curl">curl -fsSL https://hub.felhom.eu/api/v1/config/{{.CustomerID}} -H "X-Retrieval-Password: <YOUR-RETRIEVAL-PASSWORD>" -o controller.yaml</code>
|
||||
<button type="button" class="copy-btn" onclick="copyText('cmd-curl')" title="Copy">⎘</button>
|
||||
</div>
|
||||
</section>
|
||||
@@ -694,6 +696,32 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Masked-secret controls (retrieval password): the value lives in data-secret and is masked in
|
||||
// the visible node by default; Reveal toggles it, Copy copies the real value. It is never baked
|
||||
// into a copyable command (see the Option-3 placeholder).
|
||||
var _secretMask = '•'.repeat(16);
|
||||
function toggleSecret(elementId) {
|
||||
var el = document.getElementById(elementId);
|
||||
var btn = document.getElementById('reveal-' + elementId);
|
||||
if (el.dataset.revealed === '1') {
|
||||
el.textContent = _secretMask; el.dataset.revealed = '0';
|
||||
if (btn) btn.textContent = 'Reveal';
|
||||
} else {
|
||||
el.textContent = el.getAttribute('data-secret') || ''; el.dataset.revealed = '1';
|
||||
if (btn) btn.textContent = 'Hide';
|
||||
}
|
||||
}
|
||||
function copySecret(elementId) {
|
||||
var el = document.getElementById(elementId);
|
||||
var val = (el.getAttribute('data-secret') || '').trim();
|
||||
navigator.clipboard.writeText(val).then(function() {
|
||||
var btn = el.parentElement.querySelectorAll('.copy-btn');
|
||||
var b = btn[btn.length - 1]; var orig = b.innerHTML;
|
||||
b.innerHTML = '✓';
|
||||
setTimeout(function() { b.innerHTML = orig; }, 1500);
|
||||
});
|
||||
}
|
||||
|
||||
function disableGeo(customerID) {
|
||||
if (!confirm('Összes geo-korlátozás eltávolítása?\n\nEz közvetlenül törli a Cloudflare WAF szabályokat és értesíti a controllert.')) return;
|
||||
var btn = document.getElementById('btn-geo-disable');
|
||||
|
||||
Reference in New Issue
Block a user