Wire WireGuard peer DNS to ControlD profiles via ctrld
When creating a WireGuard peer on OPNsense: - Client config DNS now points to OPNsense's IP (not tunnel gateway) so DNS flows: client → OPNsense → Unbound → ctrld → ControlD - New dns_profile field: select which ControlD profile applies to VPN clients (default: "house" for VLAN 99) - Generates ctrld.toml instructions for WireGuard tunnel subnet routing — tells user what to add so ctrld routes VPN DNS queries to the correct ControlD profile - QR modal now shows ControlD setup instructions alongside the WireGuard config This solves the Android Private DNS conflict: WireGuard's DNS setting overrides Android's Private DNS, pointing to OPNsense which runs Unbound → ctrld. No Private DNS toggle needed on the phone. Multi-VLAN access for VPN peers works because the peer is on the WireGuard interface (not on any VLAN). OPNsense routes between the tunnel and VLANs per firewall rules. VLAN isolation preserved. https://claude.ai/code/session_01Do9bsN39MTuy2GVv7yzSrE
This commit is contained in:
+32
-4
@@ -1913,7 +1913,7 @@ function DeviceAccessTab({ session, onNeedAuth, backendOk }) {
|
||||
// WIREGUARD TAB
|
||||
// ══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
function QRModal({ config, name, onClose }) {
|
||||
function QRModal({ config, name, ctrldNote, onClose }) {
|
||||
// Render QR using a simple API since we can't use native qrencode in browser
|
||||
const [qrUrl, setQrUrl] = useState('');
|
||||
useEffect(() => {
|
||||
@@ -1936,6 +1936,14 @@ function QRModal({ config, name, onClose }) {
|
||||
maxHeight:120,overflowY:"auto",marginBottom:12}}>
|
||||
{config}
|
||||
</div>
|
||||
{ctrldNote && (
|
||||
<div style={{background:"var(--b1)",borderRadius:4,padding:8,fontSize:10,
|
||||
color:"var(--dm)",textAlign:"left",whiteSpace:"pre-wrap",
|
||||
marginBottom:12,lineHeight:1.6}}>
|
||||
<div style={{fontWeight:700,color:"var(--ac)",marginBottom:4}}>ControlD DNS Setup</div>
|
||||
{ctrldNote}
|
||||
</div>
|
||||
)}
|
||||
<div style={{display:"flex",gap:8,justifyContent:"center"}}>
|
||||
<button className="btn bg" onClick={()=>{
|
||||
const a=document.createElement('a');
|
||||
@@ -2200,6 +2208,7 @@ function WireGuardTab({ session, onNeedAuth, backendOk, vlans = [] }) {
|
||||
});
|
||||
const [opnPeerName, setOpnPeerName] = useState('');
|
||||
const [opnVlans, setOpnVlans] = useState([]); // checked VLAN IDs
|
||||
const [opnDnsProfile, setOpnDnsProfile] = useState('house'); // ControlD profile for VPN clients
|
||||
const [opnAdding, setOpnAdding] = useState(false);
|
||||
const [opnQr, setOpnQr] = useState(null); // { config, name }
|
||||
|
||||
@@ -2294,9 +2303,10 @@ function WireGuardTab({ session, onNeedAuth, backendOk, vlans = [] }) {
|
||||
const r = await API("/opnsense/wireguard/add-peer", {
|
||||
method:"POST",
|
||||
body:{ token: session.token, name: opnPeerName.trim(),
|
||||
allowed_vlans: opnVlans, vlan_subnets }
|
||||
allowed_vlans: opnVlans, vlan_subnets,
|
||||
dns_profile: opnDnsProfile }
|
||||
});
|
||||
setOpnQr({ config: r.config, name: r.name });
|
||||
setOpnQr({ config: r.config, name: r.name, ctrld_note: r.ctrld_note });
|
||||
setOpnPeerName(''); setOpnVlans([]);
|
||||
await loadOpnWg();
|
||||
} catch(e) { setOpnError(e.message); }
|
||||
@@ -2363,6 +2373,7 @@ function WireGuardTab({ session, onNeedAuth, backendOk, vlans = [] }) {
|
||||
opnSetup={opnSetup} setOpnSetup={setOpnSetup}
|
||||
opnPeerName={opnPeerName} setOpnPeerName={setOpnPeerName}
|
||||
opnVlans={opnVlans} opnAdding={opnAdding}
|
||||
opnDnsProfile={opnDnsProfile} setOpnDnsProfile={setOpnDnsProfile}
|
||||
session={session} onNeedAuth={onNeedAuth} vlans={vlans}
|
||||
onToggleVlan={opnToggleVlan} onSetupServer={opnSetupServer}
|
||||
onDeleteServer={opnDeleteServer} onAddPeer={opnAddPeer}
|
||||
@@ -2511,6 +2522,8 @@ function WireGuardTab({ session, onNeedAuth, backendOk, vlans = [] }) {
|
||||
setOpnPeerName={setOpnPeerName}
|
||||
opnVlans={opnVlans}
|
||||
opnAdding={opnAdding}
|
||||
opnDnsProfile={opnDnsProfile}
|
||||
setOpnDnsProfile={setOpnDnsProfile}
|
||||
session={session}
|
||||
onNeedAuth={onNeedAuth}
|
||||
vlans={vlans}
|
||||
@@ -2524,7 +2537,8 @@ function WireGuardTab({ session, onNeedAuth, backendOk, vlans = [] }) {
|
||||
/>
|
||||
)}
|
||||
|
||||
{opnQr && <QRModal config={opnQr.config} name={opnQr.name} onClose={()=>setOpnQr(null)}/>}
|
||||
{opnQr && <QRModal config={opnQr.config} name={opnQr.name}
|
||||
ctrldNote={opnQr.ctrld_note} onClose={()=>setOpnQr(null)}/>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2533,6 +2547,7 @@ function WireGuardTab({ session, onNeedAuth, backendOk, vlans = [] }) {
|
||||
function OPNsenseWGSection({
|
||||
opnWg, opnLoading, opnError, opnSetup, setOpnSetup,
|
||||
opnPeerName, setOpnPeerName, opnVlans, opnAdding,
|
||||
opnDnsProfile, setOpnDnsProfile,
|
||||
session, onNeedAuth, vlans,
|
||||
onToggleVlan, onSetupServer, onDeleteServer,
|
||||
onAddPeer, onRevokePeer, onShowConf, onRefresh,
|
||||
@@ -2779,6 +2794,19 @@ function OPNsenseWGSection({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{marginTop:10}}>
|
||||
<div className="field" style={{margin:0,maxWidth:260}}>
|
||||
<label>ControlD DNS Profile (applied via ctrld)</label>
|
||||
<input value={opnDnsProfile}
|
||||
onChange={e=>setOpnDnsProfile(e.target.value)}
|
||||
placeholder="house"/>
|
||||
</div>
|
||||
<div style={{fontSize:11,color:"var(--dm)",marginTop:4}}>
|
||||
VPN clients use OPNsense DNS → Unbound → ctrld → ControlD.
|
||||
This profile applies to the WireGuard tunnel subnet.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{marginTop:12,display:"flex",gap:8,alignItems:"center"}}>
|
||||
{!session && (
|
||||
<button className="btn bg" style={{fontSize:11,padding:"4px 10px"}}
|
||||
|
||||
Reference in New Issue
Block a user