VPN tab: prioritize OPNsense WireGuard, contextual descriptions

When OPNsense WG plugin is detected:
- Show recommendation banner at top explaining why router-level VPN
  is better than running it on the management PC
- OPNsense WG section appears first (recommended path)
- Local WG panels relabeled as "backup / this machine only"
- Local WG description changes to explain the limitation (VLAN 99 only)

When OPNsense is not connected:
- Local WG shown normally as the primary option
- Tip nudge added pointing to DHCP tab to unlock router-level VPN
- OPNsense WG section shown below as a setup prompt

In both cases:
- SSH Tunnel renamed to "Emergency Fallback" with clearer description
- Pre-select servers VLAN when OPNsense WG server becomes available
  (most users want remote access to services, not IoT/cameras)
- Connected Peers / Clients panels prefixed "Local WG —" for clarity

https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6
This commit is contained in:
Claude
2026-03-23 15:57:13 +00:00
parent f9bb4b26cc
commit 7fe42416d8
2 changed files with 104 additions and 37 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+103 -36
View File
@@ -1874,21 +1874,86 @@ function WireGuardTab({ session, onNeedAuth, backendOk, vlans = [] }) {
} catch(e) { setOpnError(e.message); }
};
// Pre-select servers VLAN when OPNsense WG server becomes available
useEffect(() => {
if (opnWg?.server && opnVlans.length === 0) {
const serversVlan = vlans.find(v =>
v.name?.toLowerCase().includes("server") && v.id !== 99
);
if (serversVlan) setOpnVlans([serversVlan.id]);
}
}, [opnWg?.server]);
const opnAvailable = opnWg?.opnsense_configured && opnWg?.plugin_installed;
return (
<div className="main" style={{flexDirection:"column",gap:12}}>
{/* ── Recommendation banner when OPNsense WG is available ──── */}
{opnAvailable && (
<div style={{
background:"rgba(0,229,255,.06)",border:"1px solid rgba(0,229,255,.25)",
borderRadius:6,padding:"12px 16px",display:"flex",gap:12,alignItems:"flex-start",
}}>
<div style={{fontSize:18,lineHeight:1,color:"var(--ac)"}}>◈</div>
<div style={{flex:1}}>
<div style={{fontWeight:700,color:"var(--ac)",marginBottom:4}}>
OPNsense WireGuard available — recommended
</div>
<div style={{fontSize:12,color:"var(--dm)",lineHeight:1.7}}>
Your OPNsense has the WireGuard plugin. Running VPN on the router is
better than here: it stays on when this PC is off, your firewall and VPN
live in the same place, and every VLAN can be reached remotely.
The local option below still works as a backup.
</div>
</div>
</div>
)}
{/* ── OPNsense WG first when available ─────────────────────── */}
{opnAvailable && (
<OPNsenseWGSection
opnWg={opnWg} opnLoading={opnLoading} opnError={opnError}
opnSetup={opnSetup} setOpnSetup={setOpnSetup}
opnPeerName={opnPeerName} setOpnPeerName={setOpnPeerName}
opnVlans={opnVlans} opnAdding={opnAdding}
session={session} onNeedAuth={onNeedAuth} vlans={vlans}
onToggleVlan={opnToggleVlan} onSetupServer={opnSetupServer}
onDeleteServer={opnDeleteServer} onAddPeer={opnAddPeer}
onRevokePeer={opnRevokePeer} onShowConf={opnShowConf} onRefresh={loadOpnWg}
/>
)}
{/* ── Local WireGuard ──────────────────────────────────────── */}
<div className="panel">
<div className="ph">◈ WireGuard VPN
<div className="ph">
{opnAvailable ? "◈ Local WireGuard — backup / this machine only" : "◈ WireGuard VPN"}
<span style={{marginLeft:"auto",fontFamily:"var(--mono)",fontSize:10,
color:isRunning?"var(--ok)":"var(--err)"}}>
{isRunning ? "● Running" : "○ Not running"}
</span>
</div>
<div className="pb">
<div style={{fontSize:12,color:"var(--dm)",lineHeight:1.7,marginBottom:12}}>
WireGuard lets you connect from anywhere — home WiFi, coffee shop, anywhere —
and reach the switch manager as if you were on the management network.
Each device gets its own key. Revoking a key disconnects that device immediately.
</div>
{opnAvailable ? (
<div style={{fontSize:12,color:"var(--dm)",lineHeight:1.7,marginBottom:12}}>
Run WireGuard on this management PC instead of OPNsense.
Use this as a backup when OPNsense is being reconfigured, or if you
prefer not to touch the router. Clients can only reach VLAN 99
(management network) from here — they cannot reach other VLANs
unless this PC also routes that traffic.
</div>
) : (
<div style={{fontSize:12,color:"var(--dm)",lineHeight:1.7,marginBottom:12}}>
WireGuard lets you connect from anywhere — coffee shop, hotel, cellular —
and reach this switch manager as if you were on VLAN 99.
Each device gets its own key; revoking a key disconnects it immediately.
<br/><br/>
<span style={{color:"var(--warn)"}}>
Tip: Connect OPNsense in the DHCP tab to unlock router-level WireGuard —
always on, handles all VLANs, no dependency on this machine.
</span>
</div>
)}
{!isRunning && (
<div style={{background:"rgba(255,23,68,.07)",border:"1px solid rgba(255,23,68,.2)",
borderRadius:4,padding:"10px 14px",marginBottom:12,fontSize:11,color:"var(--err)"}}>
@@ -1903,7 +1968,7 @@ function WireGuardTab({ session, onNeedAuth, backendOk, vlans = [] }) {
{/* Connected peers */}
{isRunning && status?.peers?.length > 0 && (
<div className="panel">
<div className="ph">◈ Connected Peers ({status.peers.length})</div>
<div className="ph">◈ Local WG — Connected Peers ({status.peers.length})</div>
<div className="pb">
{status.peers.map((p,i) => (
<div key={i} style={{padding:"8px 0",borderBottom:"1px solid var(--b1)",
@@ -1930,7 +1995,7 @@ function WireGuardTab({ session, onNeedAuth, backendOk, vlans = [] }) {
{/* Client management */}
<div className="panel">
<div className="ph">◈ VPN Clients</div>
<div className="ph">◈ Local WG — Clients</div>
<div className="pb">
{clients.length === 0 && <div className="empty" style={{padding:"16px 0"}}>No clients configured yet.</div>}
{clients.map(c => (
@@ -1967,49 +2032,51 @@ function WireGuardTab({ session, onNeedAuth, backendOk, vlans = [] }) {
</div>
</div>
{/* SSH tunnel info */}
{/* SSH tunnel fallback */}
<div className="panel">
<div className="ph">◈ SSH Tunnel — Power User Alternative</div>
<div className="ph">◈ SSH Tunnel — Emergency Fallback</div>
<div className="pb">
<div style={{fontSize:12,color:"var(--dm)",lineHeight:1.7,marginBottom:8}}>
If WireGuard is not available, SSH port forwarding gives secure access
in one command. Run this on your remote machine:
If WireGuard is down or misconfigured, SSH port forwarding reaches the
switch manager in one command — no daemon, no keys to manage.
</div>
<div style={{background:"#060809",borderRadius:4,padding:"10px 14px",
fontFamily:"var(--mono)",fontSize:12,color:"#a0b0c0",marginBottom:8}}>
ssh -L 8765:localhost:8765 user@your-management-computer-ip
</div>
<div style={{fontSize:11,color:"var(--dm)"}}>
Then open <span style={{color:"var(--ac)",fontFamily:"var(--mono)"}}>http://localhost:8765</span> in your browser.
The management computer needs SSH accessible from outside (key auth only, consider fail2ban).
Then open <span style={{color:"var(--ac)",fontFamily:"var(--mono)"}}>http://localhost:8765</span>.
Requires SSH accessible from outside (key auth only — consider fail2ban).
</div>
</div>
</div>
{qrModal && <QRModal config={qrModal.config} name={qrModal.name} onClose={()=>setQrModal(null)}/>}
{/* ── OPNsense WireGuard ─────────────────────────────────────── */}
<OPNsenseWGSection
opnWg={opnWg}
opnLoading={opnLoading}
opnError={opnError}
opnSetup={opnSetup}
setOpnSetup={setOpnSetup}
opnPeerName={opnPeerName}
setOpnPeerName={setOpnPeerName}
opnVlans={opnVlans}
opnAdding={opnAdding}
session={session}
onNeedAuth={onNeedAuth}
vlans={vlans}
onToggleVlan={opnToggleVlan}
onSetupServer={opnSetupServer}
onDeleteServer={opnDeleteServer}
onAddPeer={opnAddPeer}
onRevokePeer={opnRevokePeer}
onShowConf={opnShowConf}
onRefresh={loadOpnWg}
/>
{/* OPNsense WG shown below local when not yet available (nudge to set up) */}
{!opnAvailable && (
<OPNsenseWGSection
opnWg={opnWg}
opnLoading={opnLoading}
opnError={opnError}
opnSetup={opnSetup}
setOpnSetup={setOpnSetup}
opnPeerName={opnPeerName}
setOpnPeerName={setOpnPeerName}
opnVlans={opnVlans}
opnAdding={opnAdding}
session={session}
onNeedAuth={onNeedAuth}
vlans={vlans}
onToggleVlan={opnToggleVlan}
onSetupServer={opnSetupServer}
onDeleteServer={opnDeleteServer}
onAddPeer={opnAddPeer}
onRevokePeer={opnRevokePeer}
onShowConf={opnShowConf}
onRefresh={loadOpnWg}
/>
)}
{opnQr && <QRModal config={opnQr.config} name={opnQr.name} onClose={()=>setOpnQr(null)}/>}
</div>