Files
wg-portal/master/documentation/usage/backends/index.html
T

41 lines
37 KiB
HTML
Raw Normal View History

<!doctype html><html lang=en class=no-js> <head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="Manage WireGuard Peers and Interface using a beautiful and simple web UI."><link href=https://wgportal.org/master/documentation/usage/backends/ rel=canonical><link href=../general/ rel=prev><link href=../authentication/ rel=next><link rel=icon href=../../../assets/images/favicon-large.png><meta name=generator content="mkdocs-1.6.1, mkdocs-material-9.7.7"><title>Backends - WireGuard Portal</title><link rel=stylesheet href=../../../assets/stylesheets/main.ec1eaa64.min.css><link rel=stylesheet href=../../../assets/stylesheets/palette.ab4e12ef.min.css><link rel=stylesheet href=../../../stylesheets/extra.css><link rel=stylesheet href=../../../stylesheets/img-comparison-slider.css><script>__md_scope=new URL("../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script><meta property=og:type content=website><meta property=og:title content="Backends - WireGuard Portal"><meta property=og:description content="Manage WireGuard Peers and Interface using a beautiful and simple web UI."><meta property=og:image content=https://wgportal.org/master/assets/images/social/documentation/usage/backends.png><meta property=og:image:type content=image/png><meta property=og:image:width content=1200><meta property=og:image:height content=630><meta content=https://wgportal.org/master/documentation/usage/backends/ property=og:url><meta property=twitter:card content=summary_large_image><meta property=twitter:title content="Backends - WireGuard Portal"><meta property=twitter:description content="Manage WireGuard Peers and Interface using a beautiful and simple web UI."><meta property=twitter:image content=https://wgportal.org/master/assets/images/social/documentation/usage/backends.png></head> <body dir=ltr data-md-color-scheme=default data-md-color-primary=white data-md-color-accent=indigo> <input class=md-toggle data-md-toggle=drawer type=checkbox id=__drawer autocomplete=off> <input class=md-toggle data-md-toggle=search type=checkbox id=__search autocomplete=off> <label class=md-overlay for=__drawer></label> <div data-md-component=skip> <a href=#backends class=md-skip> Skip to content </a> </div> <div data-md-component=announce> </div> <div data-md-color-scheme=default data-md-component=outdated hidden> </div> <header class=md-header data-md-component=header> <nav class="md-header__inner md-grid" aria-label=Header> <a href=../../.. title="WireGuard Portal" class="md-header__button md-logo" aria-label="WireGuard Portal" data-md-component=logo> <img src=../../../assets/images/logo.svg alt=logo> </a> <label class="md-header__button md-icon" for=__drawer> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg> </label> <div class=md-header__title data-md-component=header-title> <div class=md-header__ellipsis> <div class=md-header__topic> <span class=md-ellipsis> WireGuard Portal </span> </div> <div class=md-header__topic data-md-component=header-topic> <span class=md-ellipsis> Backends </span> </div> </div> </div> <label class="md-header__button md-icon" for=__search> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg> </label> <div class=md-search data-md-component=search role=dialog> <label class=md-search__overlay for=__search></label> <div class=md-search__inner role=search> <form class=md-search__form name=search> <input type=text class=md-search__input name=query aria-label=Search placeholder=Search autocapitalize=off autocorrect=off autocomplete=off spellcheck=false data-md-compo
<span class=w> </span><span class=c1># default backend decides where new interfaces are created</span>
<span class=w> </span><span class=nt>default</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">mikrotik-prod</span>
<span class=w> </span><span class=nt>mikrotik</span><span class=p>:</span>
<span class=w> </span><span class="p p-Indicator">-</span><span class=w> </span><span class=nt>id</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">mikrotik-prod</span><span class=w> </span><span class=c1># unique id, not "local"</span>
<span class=w> </span><span class=nt>display_name</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">RouterOS RB5009</span><span class=w> </span><span class=c1># optional nice name</span>
<span class=w> </span><span class=nt>api_url</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">https://10.10.10.10/rest</span>
<span class=w> </span><span class=nt>api_user</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">wgportal</span>
<span class=w> </span><span class=nt>api_password</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">a-super-secret-password</span>
<span class=w> </span><span class=nt>api_verify_tls</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">true</span><span class=w> </span><span class=c1># set to false only if using self-signed during testing</span>
<span class=w> </span><span class=nt>api_timeout</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">30s</span><span class=w> </span><span class=c1># maximum request duration</span>
<span class=w> </span><span class=nt>concurrency</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">5</span><span class=w> </span><span class=c1># limit parallel REST calls to device</span>
<span class=w> </span><span class=nt>debug</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">false</span><span class=w> </span><span class=c1># verbose logging for this backend</span>
</code></pre></div> <h3 id=known-limitations>Known limitations:</h3> <ul> <li>The MikroTik backend is still in beta. Some features may not work as expected.</li> <li>Not all WireGuard Portal features are supported yet (e.g., no support for interface hooks)</li> </ul> <h2 id=configuring-pfsense-backends>Configuring pfSense backends</h2> <blockquote> <p><img alt=⚠ class=twemoji src=https://cdn.jsdelivr.net/gh/jdecked/twemoji@16.0.1/assets/svg/26a0.svg title=:warning:> The pfSense backend is currently <strong>alpha</strong>. Only basic interface and peer CRUD are supported. Traffic statistics (rx/tx, last handshake) are not exposed by the pfSense REST API and will show as empty.</p> </blockquote> <p>The pfSense backend talks to the pfSense REST API (pfSense Plus / CE with the REST API package installed). Point the backend at the appliance hostname without appending <code>/api/v2</code> — the portal appends <code>/api/v2</code> automatically. wg-portal is developed for and tested against REST API v2.8.0.</p> <h3 id=prerequisites-on-pfsense>Prerequisites on pfSense:</h3> <ul> <li>pfSense with the REST API package enabled (<code>System -&gt; API</code>) and WireGuard configured.</li> <li>An API key with permissions for WireGuard endpoints. If you use a read-only key, set <code>core.restore_state: false</code> in <code>config.yml</code> to avoid write attempts at startup.</li> <li>HTTPS recommended; set <code>api_verify_tls: false</code> only for lab/self-signed setups.</li> </ul> <p>Example WireGuard Portal configuration:</p> <div class=highlight><pre><span></span><code><span class=nt>backend</span><span class=p>:</span>
<span class=w> </span><span class=c1># default backend decides where new interfaces are created</span>
<span class=w> </span><span class=nt>default</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">pfsense1</span>
<span class=w> </span><span class=nt>pfsense</span><span class=p>:</span>
<span class=w> </span><span class="p p-Indicator">-</span><span class=w> </span><span class=nt>id</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">pfsense1</span><span class=w> </span><span class=c1># unique id, not "local"</span>
<span class=w> </span><span class=nt>display_name</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">Main pfSense</span><span class=w> </span><span class=c1># optional nice name</span>
<span class=w> </span><span class=nt>api_url</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">https://pfsense.example.com</span><span class=w> </span><span class=c1># no trailing /api/v2</span>
<span class=w> </span><span class=nt>api_key</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">your-api-key</span>
<span class=w> </span><span class=nt>api_verify_tls</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class=w> </span><span class=nt>api_timeout</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">30s</span>
<span class=w> </span><span class=nt>concurrency</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">5</span>
<span class=w> </span><span class=nt>debug</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">false</span>
</code></pre></div> <h3 id=known-limitations_1>Known limitations:</h3> <ul> <li>Alpha quality: behavior and API coverage may change.</li> <li>Statistics (rx/tx bytes, last handshake) are not available from the pfSense REST API today.</li> </ul> <h2 id=configuring-opnsense-backends>Configuring OPNsense backends</h2> <blockquote> <p><img alt=⚠ class=twemoji src=https://cdn.jsdelivr.net/gh/jdecked/twemoji@16.0.1/assets/svg/26a0.svg title=:warning:> The OPNsense backend is currently <strong>alpha</strong>. Interface and peer CRUD are supported, as are <strong>per-peer</strong> traffic statistics (rx/tx bytes and last handshake), which the pfSense backend cannot provide. Interface-level byte counters are not reported, because OPNsense exposes counters per peer only. Interface hooks, DNS push and ping are not supported.</p> </blockquote> <p>The OPNsense backend talks to the WireGuard API that is part of <strong>OPNsense core</strong>. Unlike the pfSense backend, no add-on package needs to be installed — a stock OPNsense install already answers <code>/api/wireguard/*</code>.</p> <p>Point <code>api_url</code> at the appliance root (for example <code>https://opnsense.example.com</code>); the portal appends the API paths itself.</p> <h3 id=prerequisites-on-opnsense>Prerequisites on OPNsense:</h3> <ul> <li>OPNsense 24.1 or newer, where WireGuard is part of the base system. Developed and tested against 26.7.</li> <li>An API key/secret pair, created under <code>System -&gt; Access -&gt; Users -&gt; &lt;user&gt; -&gt; API keys</code>. OPNsense shows the secret only once, at creation time.</li> <li>The user needs permission for the WireGuard and firewall pages it should manage.</li> <li>HTTPS recommended; set <code>api_verify_tls: false</code> only for lab/self-signed setups.</li> </ul> <p>Example WireGuard Portal configuration:</p> <div class=highlight><pre><span></span><code><span class=nt>backend</span><span class=p>:</span>
<span class=w> </span><span class=c1># default backend decides where new interfaces are created</span>
<span class=w> </span><span class=nt>default</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">opnsense1</span>
<span class=w> </span><span class=nt>opnsense</span><span class=p>:</span>
<span class=w> </span><span class="p p-Indicator">-</span><span class=w> </span><span class=nt>id</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">opnsense1</span><span class=w> </span><span class=c1># unique id, not "local"</span>
<span class=w> </span><span class=nt>display_name</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">Edge firewall</span><span class=w> </span><span class=c1># optional nice name</span>
<span class=w> </span><span class=nt>api_url</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">https://opnsense.example.com</span><span class=w> </span><span class=c1># appliance root, no /api suffix</span>
<span class=w> </span><span class=nt>api_key</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">your-api-key</span>
<span class=w> </span><span class=nt>api_secret</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">your-api-secret</span>
<span class=w> </span><span class=nt>api_verify_tls</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">true</span>
<span class=w> </span><span class=nt>api_timeout</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">30s</span>
<span class=w> </span><span class=nt>debug</span><span class=p>:</span><span class=w> </span><span class="l l-Scalar l-Scalar-Plain">false</span>
</code></pre></div> <h3 id=behaviour-worth-knowing>Behaviour worth knowing:</h3> <ul> <li><strong>Interface naming.</strong> An OPNsense tunnel has both an instance number and a free-text name. WireGuard Portal uses the resulting device name (<code>wg0</code>, <code>wg1</code>, ...) as the interface identifier, so an interface imported from OPNsense looks the same as one managed by the <code>local</code> backend. The free-text name becomes the display name.</li> <li><strong>Changes are staged.</strong> OPNsense applies WireGuard configuration only when the service is reconfigured, which the backend does after every change. Reconfiguring does <strong>not</strong> drop or rekey sessions of peers that are already connected, so adding a peer is safe against a live VPN.</li> <li><strong>The service is enabled automatically.</strong> Bringing an interface up switches on the global WireGuard service if it is off, because a tunnel configured while the service is disabled stays down without reporting an error anywhere. The backend never disables the service.</li> <li><strong>Firewall rules are not managed.</strong> A newly created tunnel has no pass rule, so peers will complete a handshake but carry no traffic until you add a rule on the <code>WireGuard (Group)</code> interface. This matches the pfSense backend, which also leaves firewall rules to the administrator.</li> </ul> <h3 id=known-limitations_2>Known limitations:</h3> <ul> <li>Alpha quality: behavior and API coverage may change.</li> <li>Interface hooks (<code>PreUp</code>/<code>PostUp</code>/...) are not supported; OPNsense has no API equivalent.</li> <li>DNS settings pushed to clients are a property of the tunnel in OPNsense and are not managed by the portal.</li> <li><code>PingAddresses</code> is not implemented.</li> </ul> </article> </div> <script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script> </div> </main> <!-- Application footer --> <footer class=md-footer> <!-- Further information --> <div class="md-footer-meta md-typeset" style="background-color: #fff;"> <div class="md-footer-meta__inner md-grid" style="background-color: #fff;"> <!-- Copyright and theme information --> <div class=md-footer-copyright> <div class=md-footer-copyright__highlight style="color: rgb(38, 38, 38);"> Copyright &copy; 2023-2025 WireGuard Portal Project </div> <div style="color: rgb(38, 38, 38);"> Made with <a href=https://squidfunk.github.io/mkdocs-material/ target=_blank rel=noopener style="color: black;"> Material for MkDocs </a> </div> </div> <!-- Social links --> <div class=md-social> <a href=https://github.com/h44z/wg-portal target=_blank rel=noopener title=github.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 512 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M202.1 328.7c0 20.9-10.9 55.1-36.7 55.1s-36.7-34.2-36.7-55.1 10.9-55.1 36.7-55.1 36.7 34.2 36.7 55.1M496 278.2c0 31.9-3.2 65.7-17.5 95-37.9 76.6-142.1 74.8-216.7 74.8-75.8 0-186.2 2.7-225.6-74.8-14.6-29-20.2-63.1-20.2-95 0-41.9 13.9-81.5 41.5-113.6-5.2-15.8-7.7-32.4-7.7-48.8 0-21.5 4.9-32.3 14.6-51.8 45.3 0 74.3 9 108.8 36 29-6.9 58.8-10 88.7-10 27 0 54.2 2.9 80.4 9.2 34-26.7 63-35.2 107.8-35.2 9.8 19.5 14.6 30.3 14.6 51.8 0 16.4-2.6 32.7-7.7 48.2 27.5 32.4 39 72.3 39 114.2m-64.3 50.5c0-43.9-26.7-82.6-73.5-82.6-18.9 0-37 3.4-56 6-14.9 2.3-29.8 3.2-45.1 3.2-15.2 0-30.1-.9-45.1-3.2-18.7-2.6-37-6-56-6-46.8 0-73.5 38.7-73.5 82.6 0 87.8 80.4 101.3 150.4 101.3h48.2c70.3 0 150.6-13.4 150.6-101.3m-82.6-55.1c-25.8 0-36.7 34.2-36.7 55.1s10.9 55.1 36.7 55.1 36.7-34.2 36.7-55.1-10.9-55.1-36.7-55.1"/></svg> </a> <a href=https://hub.docker.com/r/wgportal/wg-portal target=_blank rel=noopener title=hub.docker.com class=md-social__link> <svg xmlns=http://www.w3.org/2000/svg viewbox="0 0 640 512"><!-- Font Awesome Free 7.1.0 by @fontawesome - https