net/wireguard - remove wg-quick dependency and drop go support (#3556)

net/wireguard - removing wg-quick and go support.

This commits adds the following:

* Remove wireguard-go support and cleanup some go specific code as it's not being used anymore anyway
* Service control handler similar to OpenVPN, which offers control per instance/interface and keeps track of changed interfaces (configure only restarts the changed ones).
* Add some basic logging for the service handling and a view to inspect it.
* Configuration logs are being flushed to the correct log automatically as mwexecf() sends errors to syslog (which in this scope sends to wireguard)
* Reimplement https://github.com/WireGuard/wireguard-tools/tree/master/contrib/reresolve-dns using Python in reresolve-dns.py
* Enforce wireguard-tools rc script to be disabled when still installed, this should prevent bootup issues
* Move 'interface' calculated field to model for easy reusability
* Change plugin maintainer


---------

Co-authored-by: Franco Fichtner <franco@opnsense.org>
This commit is contained in:
Ad Schellevis
2023-08-22 14:39:53 +02:00
committed by GitHub
parent bac3bc407b
commit 86c9e5ccc8
21 changed files with 484 additions and 242 deletions
+3 -4
View File
@@ -1,9 +1,8 @@
PLUGIN_NAME= wireguard
PLUGIN_VERSION= 1.13
PLUGIN_REVISION= 7
PLUGIN_VERSION= 2.0.d
PLUGIN_COMMENT= WireGuard VPN service kernel implementation
PLUGIN_DEPENDS= wireguard-kmod wireguard-tools
PLUGIN_DEPENDS= wireguard-kmod
PLUGIN_CONFLICTS= wireguard-go
PLUGIN_MAINTAINER= m.muenz@gmail.com
PLUGIN_MAINTAINER= ad@opnsense.org
.include "../../Mk/plugins.mk"
+11
View File
@@ -16,6 +16,17 @@ WWW: https://www.wireguard.com/
Changelog
---------
2.0
* Remove wireguard-go support and cleanup some go specific code as it's not being used anymore anyway
* Service control handler similar to OpenVPN, which offers control per instance/interface and keeps track of changed interfaces (configure only restarts the changed ones).
* Add some basic logging for the service handling and a view to inspect it.
* Configuration logs are being flushed to the correct log automatically as mwexecf() sends errors to syslog (which in this scope sends to wireguard)
* Reimplement https://github.com/WireGuard/wireguard-tools/tree/master/contrib/reresolve-dns using Python in reresolve-dns.py
* Enforce wireguard-tools rc script to be disabled when still installed, this should prevent bootup issues
* Move 'interface' calculated field to model for easy reusability
* Change plugin maintainer
1.13
* Reworked widget and assorted cleanups (contributed by Patrik Kernstock)
@@ -1,6 +1,7 @@
<?php
/*
* Copyright (C) 2023 Deciso B.V.
* Copyright (C) 2018 Michael Muenz <m.muenz@gmail.com>
* All rights reserved.
*
@@ -28,8 +29,7 @@
function wireguard_enabled()
{
$model = new \OPNsense\Wireguard\General();
return (string)$model->enabled == '1';
return (string)(new \OPNsense\Wireguard\General())->enabled == '1';
}
function wireguard_services()
@@ -40,26 +40,32 @@ function wireguard_services()
return $services;
}
$service = [
'description' => gettext('WireGuard VPN'),
'configd' => [
'restart' => ['wireguard restart'],
'start' => ['wireguard start'],
'stop' => ['wireguard stop'],
],
'name' => 'wireguard-go',
];
if (file_exists('/boot/modules/if_wg.ko') || file_exists('/boot/kernel/if_wg.ko')) {
$service['name'] = 'wireguard';
$service['nocheck'] = true;
foreach ((new OPNsense\Wireguard\Server())->servers->server->iterateItems() as $key => $node) {
if (!empty((string)$node->enabled)) {
$services[] = [
'description' => "Wireguard " . htmlspecialchars($node->name),
'configd' => [
'start' => ["wireguard start {$key}"],
'restart' => ["wireguard restart {$key}"],
'stop' => ["wireguard stop {$key}"],
],
'nocheck' => true, /* no daemon to check */
'id' => $key,
'name' => "wireguard"
];
}
}
$services[] = $service;
return $services;
}
function wireguard_syslog()
{
return [
'wireguard' => ['facility' => ['wireguard']]
];
}
function wireguard_interfaces()
{
$interfaces = [];
@@ -87,11 +93,7 @@ function wireguard_xmlrpc_sync()
$result['id'] = 'wireguard';
$result['section'] = 'OPNsense.wireguard';
$result['description'] = gettext('WireGuard');
$result['services'] = ['wireguard-go'];
if (file_exists('/boot/modules/if_wg.ko') || file_exists('/boot/kernel/if_wg.ko')) {
$result['services'] = ['wireguard'];
}
$result['services'] = ['wireguard'];
return [$result];
}
@@ -1,4 +1,2 @@
#!/bin/sh
# start again to fix problems with failed name resolution (no need to restart)
configctl -dq wireguard start
configctl -dq wireguard configure
@@ -1,6 +1,7 @@
<?php
/**
* Copyright (C) 2023 Deciso B.V.
* Copyright (C) 2018 Michael Muenz <m.muenz@gmail.com>
*
* All rights reserved.
@@ -39,12 +40,14 @@ class ClientController extends ApiMutableModelControllerBase
public function searchClientAction()
{
return $this->searchBase('clients.client', array("enabled", "name", "pubkey", "tunneladdress", "serveraddress", "serverport"));
return $this->searchBase(
'clients.client',
["enabled", "name", "pubkey", "tunneladdress", "serveraddress", "serverport"]
);
}
public function getClientAction($uuid = null)
{
$this->sessionClose();
return $this->getBase('client', 'clients.client', $uuid);
}
@@ -1,6 +1,7 @@
<?php
/*
* Copyright (C) 2023 Deciso B.V.
* Copyright (C) 2018 Michael Muenz <m.muenz@gmail.com>
* All rights reserved.
*
@@ -38,17 +39,15 @@ class ServerController extends ApiMutableModelControllerBase
public function searchServerAction()
{
$search = $this->searchBase('servers.server', array("enabled", "instance", "peers", "name", "networks", "pubkey", "port", "tunneladdress"));
// prepend "wg" to all instance IDs to use as interface name
foreach ($search["rows"] as $key => $server) {
$search["rows"][$key]["interface"] = "wg" . $server["instance"];
}
$search = $this->searchBase(
'servers.server',
["enabled", "instance", "peers", "name", "networks", "pubkey", "port", "tunneladdress", 'interface']
);
return $search;
}
public function getServerAction($uuid = null)
{
$this->sessionClose();
return $this->getBase('server', 'servers.server', $uuid);
}
@@ -1,6 +1,7 @@
<?php
/*
* Copyright (C) 2023 Deciso B.V.
* Copyright (C) 2018 Michael Muenz <m.muenz@gmail.com>
* All rights reserved.
*
@@ -52,14 +53,30 @@ class ServiceController extends ApiMutableServiceControllerBase
return true;
}
/**
* @return array
*/
public function reconfigureAction()
{
if (!$this->request->isPost()) {
return ['result' => 'failed'];
}
$this->sessionClose();
$backend = new Backend();
$backend->configdRun('template reload ' . escapeshellarg(static::$internalServiceTemplate));
$backend->configdpRun('wireguard configure');
return ['result' => 'ok'];
}
/**
* show wireguard config
* @return array
*/
public function showconfAction()
{
$backend = new Backend();
$response = $backend->configdRun("wireguard showconf");
$response = (new Backend())->configdRun("wireguard showconf");
return array("response" => $response);
}
@@ -69,8 +86,7 @@ class ServiceController extends ApiMutableServiceControllerBase
*/
public function showhandshakeAction()
{
$backend = new Backend();
$response = $backend->configdRun("wireguard showhandshake");
$response = (new Backend())->configdRun("wireguard showhandshake");
return array("response" => $response);
}
}
@@ -0,0 +1,58 @@
<?php
/*
* Copyright (C) 2023 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\Wireguard\FieldTypes;
use OPNsense\Base\FieldTypes\ArrayField;
use OPNsense\Base\FieldTypes\TextField;
class ServerField extends ArrayField
{
/**
* push internal reusable properties as virtuals
*/
protected function actionPostLoadingEvent()
{
foreach ($this->internalChildnodes as $node) {
if (!$node->getInternalIsVirtual()) {
$files = [
'cnfFilename' => "/usr/local/etc/wireguard/wg{$node->instance}.conf",
'statFilename' => "/usr/local/etc/wireguard/wg{$node->instance}.stat",
'interface' => "wg{$node->instance}",
];
foreach ($files as $name => $payload) {
$new_item = new TextField();
$new_item->setInternalIsVirtual();
$new_item->setValue($payload);
$node->addChildNode($name, $new_item);
}
}
}
return parent::actionPostLoadingEvent();
}
}
@@ -1,5 +1,8 @@
<menu>
<VPN>
<WireGuard cssClass="fa fa-lock fa-fw" url="/ui/wireguard/general/index" order="150" />
<WireGuard cssClass="fa fa-lock fa-fw" order="150">
<Settings order="10" url="/ui/wireguard/general/index"/>
<LogFile order="70" VisibleName="Log File" url="/ui/diagnostics/log/core/wireguard"/>
</WireGuard>
</VPN>
</menu>
@@ -4,7 +4,7 @@
<version>0.0.4</version>
<items>
<servers>
<server type="ArrayField">
<server type=".\ServerField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
@@ -1,5 +1,5 @@
{#
# OPNsense (c) 2014-2018 by Deciso B.V.
# OPNsense (c) 2014-2023 by Deciso B.V.
# OPNsense (c) 2018 Michael Muenz <m.muenz@gmail.com>
# All rights reserved.
#
@@ -25,6 +25,66 @@
# POSSIBILITY OF SUCH DAMAGE.
#}
<script>
$( document ).ready(function() {
var data_get_map = {'frm_general_settings':"/api/wireguard/general/get"};
mapDataToFormUI(data_get_map).done(function(data){
formatTokenizersUI();
$('.selectpicker').selectpicker('refresh');
});
$("#grid-clients").UIBootgrid(
{
'search':'/api/wireguard/client/searchClient',
'get':'/api/wireguard/client/getClient/',
'set':'/api/wireguard/client/setClient/',
'add':'/api/wireguard/client/addClient/',
'del':'/api/wireguard/client/delClient/',
'toggle':'/api/wireguard/client/toggleClient/'
}
);
$("#grid-servers").UIBootgrid(
{
'search':'/api/wireguard/server/searchServer',
'get':'/api/wireguard/server/getServer/',
'set':'/api/wireguard/server/setServer/',
'add':'/api/wireguard/server/addServer/',
'del':'/api/wireguard/server/delServer/',
'toggle':'/api/wireguard/server/toggleServer/'
}
);
$("#reconfigureAct").SimpleActionButton({
onPreAction: function() {
const dfObj = new $.Deferred();
saveFormToEndpoint("/api/wireguard/general/set", 'frm_general_settings', function(){
dfObj.resolve();
});
return dfObj;
}
});
// Put API call into a function, needed for auto-refresh
function update_showconf() {
ajaxCall(url="/api/wireguard/service/showconf", sendData={}, callback=function(data,status) {
$("#listshowconf").text(data['response']);
setTimeout(update_showconf, 5000);
});
}
function update_showhandshake() {
ajaxCall(url="/api/wireguard/service/showhandshake", sendData={}, callback=function(data,status) {
$("#listshowhandshake").text(data['response']);
setTimeout(update_showhandshake, 5000);
});
}
// Call update funcs once when page loaded
update_showconf();
update_showhandshake();
});
</script>
<!-- Navigation bar -->
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
<li class="active"><a data-toggle="tab" href="#general">{{ lang._('General') }}</a></li>
@@ -38,10 +98,6 @@
<div id="general" class="tab-pane fade in active">
<div class="content-box" style="padding-bottom: 1.5em;">
{{ partial("layout_partials/base_form",['fields':generalForm,'id':'frm_general_settings'])}}
<div class="col-md-12">
<hr />
<button class="btn btn-primary" id="saveAct" type="button"><b>{{ lang._('Apply') }}</b> <i id="saveAct_progress"></i></button>
</div>
</div>
</div>
<div id="clients" class="tab-pane fade in">
@@ -68,11 +124,6 @@
</tr>
</tfoot>
</table>
<div class="col-md-12">
<hr />
<button class="btn btn-primary" id="saveAct_client" type="button"><b>{{ lang._('Apply') }}</b> <i id="saveAct_client_progress"></i></button>
<br /><br />
</div>
</div>
<div id="servers" class="tab-pane fade in">
<table id="grid-servers" class="table table-responsive" data-editDialog="dialogEditWireguardServer">
@@ -99,11 +150,6 @@
</tr>
</tfoot>
</table>
<div class="col-md-12">
<hr />
<button class="btn btn-primary" id="saveAct_server" type="button"><b>{{ lang._('Apply') }}</b> <i id="saveAct_server_progress"></i></button>
<br /><br />
</div>
</div>
<div id="showconf" class="tab-pane fade in">
<pre id="listshowconf"></pre>
@@ -113,87 +159,20 @@
</div>
</div>
<section class="page-content-main">
<div class="content-box">
<div class="col-md-12">
<br/>
<button class="btn btn-primary" id="reconfigureAct"
data-endpoint='/api/wireguard/service/reconfigure'
data-label="{{ lang._('Apply') }}"
data-error-title="{{ lang._('Error reconfiguring Wireguard') }}"
type="button"
></button>
<br/><br/>
</div>
</div>
</section>
{{ partial("layout_partials/base_dialog",['fields':formDialogEditWireguardClient,'id':'dialogEditWireguardClient','label':lang._('Edit Endpoint')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogEditWireguardServer,'id':'dialogEditWireguardServer','label':lang._('Edit Local Configuration')])}}
<script>
// Put API call into a function, needed for auto-refresh
function update_showconf() {
ajaxCall(url="/api/wireguard/service/showconf", sendData={}, callback=function(data,status) {
$("#listshowconf").text(data['response']);
});
}
function update_showhandshake() {
ajaxCall(url="/api/wireguard/service/showhandshake", sendData={}, callback=function(data,status) {
$("#listshowhandshake").text(data['response']);
});
}
$( document ).ready(function() {
var data_get_map = {'frm_general_settings':"/api/wireguard/general/get"};
mapDataToFormUI(data_get_map).done(function(data){
formatTokenizersUI();
$('.selectpicker').selectpicker('refresh');
});
$("#grid-clients").UIBootgrid(
{
'search':'/api/wireguard/client/searchClient',
'get':'/api/wireguard/client/getClient/',
'set':'/api/wireguard/client/setClient/',
'add':'/api/wireguard/client/addClient/',
'del':'/api/wireguard/client/delClient/',
'toggle':'/api/wireguard/client/toggleClient/'
}
);
$("#grid-servers").UIBootgrid(
{
'search':'/api/wireguard/server/searchServer',
'get':'/api/wireguard/server/getServer/',
'set':'/api/wireguard/server/setServer/',
'add':'/api/wireguard/server/addServer/',
'del':'/api/wireguard/server/delServer/',
'toggle':'/api/wireguard/server/toggleServer/'
}
);
// Call update funcs once when page loaded
update_showconf();
update_showhandshake();
// Call function update_neighbor with a auto-refresh of 5 seconds
setInterval(update_showconf, 5000);
setInterval(update_showhandshake, 5000);
$("#saveAct").click(function(){
saveFormToEndpoint(url="/api/wireguard/general/set", formid='frm_general_settings',callback_ok=function(){
$("#saveAct_progress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url="/api/wireguard/service/reconfigure", sendData={}, callback=function(data,status) {
$("#saveAct_progress").removeClass("fa fa-spinner fa-pulse");
});
});
});
$("#saveAct_client").click(function(){
saveFormToEndpoint(url="/api/wireguard/client/set", formid='frm_general_settings',callback_ok=function(){
$("#saveAct_client_progress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url="/api/wireguard/service/reconfigure", sendData={}, callback=function(data,status) {
$("#saveAct_client_progress").removeClass("fa fa-spinner fa-pulse");
});
});
});
$("#saveAct_server").click(function(){
saveFormToEndpoint(url="/api/wireguard/server/set", formid='frm_general_settings',callback_ok=function(){
$("#saveAct_server_progress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url="/api/wireguard/service/reconfigure", sendData={}, callback=function(data,status) {
$("#saveAct_server_progress").removeClass("fa fa-spinner fa-pulse");
});
});
});
});
</script>
@@ -1,11 +0,0 @@
#!/bin/sh
if [ -f /etc/rc.conf.d/wireguard ]; then
. /etc/rc.conf.d/wireguard
fi
for interface in ${wireguard_interfaces}; do
ifconfig ${interface} group wireguard
done
/usr/local/etc/rc.routing_configure
@@ -1,45 +0,0 @@
#!/usr/local/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (C) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
set -e
shopt -s nocasematch
shopt -s extglob
export LC_ALL=C
for CONFIG_FILE in /usr/local/etc/wireguard/*.conf; do
[[ $CONFIG_FILE =~ /?([a-zA-Z0-9_=+.-]{1,15})\.conf$ ]]
INTERFACE="${BASH_REMATCH[1]}"
process_peer() {
[[ $PEER_SECTION -ne 1 || -z $PUBLIC_KEY || -z $ENDPOINT ]] && return 0
[[ $(wg show "$INTERFACE" latest-handshakes) =~ ${PUBLIC_KEY//+/\\+}\ ([0-9]+) ]] || return 0
(( ($EPOCHSECONDS - ${BASH_REMATCH[1]}) > 135 )) || return 0
wg set "$INTERFACE" peer "$PUBLIC_KEY" endpoint "$ENDPOINT"
reset_peer_section
}
reset_peer_section() {
PEER_SECTION=0
PUBLIC_KEY=""
ENDPOINT=""
}
reset_peer_section
while read -r line || [[ -n $line ]]; do
stripped="${line%%\#*}"
key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}"
value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}"
[[ $key == "["* ]] && { process_peer; reset_peer_section; }
[[ $key == "[Peer]" ]] && PEER_SECTION=1
if [[ $PEER_SECTION -eq 1 ]]; then
case "$key" in
PublicKey) PUBLIC_KEY="$value"; continue ;;
Endpoint) ENDPOINT="$value"; continue ;;
esac
fi
done < "$CONFIG_FILE"
process_peer
done
@@ -1,4 +0,0 @@
#!/bin/sh
mkdir -p /var/run/wireguard
chmod 755 /var/run/wireguard
@@ -0,0 +1,61 @@
#!/usr/local/bin/python3
"""
Copyright (c) 2023 Ad Schellevis <ad@opnsense.org>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
"""
# Python implementation to re-resolve dns entries, for reference see:
# https://github.com/WireGuard/wireguard-tools/tree/master/contrib/reresolve-dns
import glob
import os
import subprocess
for filename in glob.glob('/usr/local/etc/wireguard/*.conf'):
this_peer = {}
ifname = os.path.basename(filename).split('.')[0]
with open(filename, 'r') as fhandle:
for line in fhandle:
if line.startswith('[Peer]'):
this_peer = {}
elif line.startswith('PublicKey'):
this_peer['PublicKey'] = line.split('=', 1)[1].strip()
elif line.startswith('Endpoint'):
this_peer['Endpoint'] = line.split('=', 1)[1].strip()
if 'Endpoint' in this_peer and 'PublicKey' in this_peer:
subprocess.run(
[
'/usr/bin/wg',
'set',
ifname,
'peer',
this_peer['PublicKey'],
'endpoint',
this_peer['Endpoint']
],
capture_output=True,
text=True
)
this_peer = {}
@@ -0,0 +1,180 @@
#!/usr/local/bin/php
<?php
/*
* Copyright (C) 2023 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
require_once('script/load_phalcon.php');
require_once('util.inc');
require_once('interfaces.inc');
/**
* mimic wg-quick behaviour, but bound to our config
*/
function wg_start($server, $fhandle)
{
if (!does_interface_exist($server->interface)) {
mwexecf('/sbin/ifconfig wg create name %s', [$server->interface]);
mwexecf('/sbin/ifconfig %s group wireguard', [$server->interface]);
}
mwexecf('/usr/bin/wg setconf %s %s', [$server->interface, $server->cnfFilename]);
foreach (explode(',', (string)$server->tunneladdress) as $alias) {
mwexecf('/sbin/ifconfig %s %s alias', [$server->interface, $alias]);
}
if (!empty((string)$server->mtu)) {
mwexecf('/sbin/ifconfig %s mtu %s', [$server->interface, $server->mtu]);
}
mwexecf('/sbin/ifconfig %s up', [$server->interface]);
if (empty((string)$server->disableroutes)) {
/**
* Add routes for all configured peers, wg-quick seems to parse 'wg show wgX allowed-ips' for this,
* but this should logically congtain the same networks.
*
* XXX: For some reason these routes look a bit off, not very well integrated into OPNsense.
* In the long run it might make sense to have some sort of pluggable model facility
* where these (and maybe other) static routes hook into.
**/
$peers = explode(',', $server->peers);
$routes_to_add = ['inet'=> [], 'inet6' => []];
foreach ((new OPNsense\Wireguard\Client())->clients->client->iterateItems() as $key => $client) {
if (empty((string)$client->enabled) || !in_array($key, $peers)) {
continue;
}
foreach (explode(',', (string)$client->tunneladdress) as $tunneladdress) {
$ipproto = strpos($tunneladdress, ":") === false ? "inet" : "inet6 ";
/* wg-quick seems to prevent /0 being routed and translates this automatically */
if (str_ends_with(trim($tunneladdress), '/0')) {
if ($ipproto == 'inet') {
array_push($routes_to_add[$ipproto], '0.0.0.0/1', '128.0.0.0/1');
} else {
array_push($routes_to_add[$ipproto], '::/1', '8000::/1');
}
} else {
$routes_to_add[$ipproto][] = $tunneladdress;
}
}
}
foreach ($routes_to_add as $ipproto => $routes) {
foreach (array_unique($routes) as $route) {
mwexecf('/sbin/route -q -n add -%s %s -interface %s', [$ipproto, $route, $server->interface]);
}
}
} elseif (!empty((string)$server->gateway)) {
/* Only bind the gateway ip to the tunnel */
$ipprefix = strpos($tunneladdress, ":") === false ? "-4" : "-6 ";
mwexecf('/sbin/route -q -n add -%s %s -iface %s', [$ipprefix, $server->gateway, $server->interface]);
}
// flush checksum to ease change detection
fseek($fhandle, 0);
ftruncate($fhandle, 0);
fwrite($fhandle, @md5_file($server->cnfFilename));
syslog(LOG_NOTICE, "Wireguard interface {$server->name} ({$server->interface}) started");
}
/**
* stop wireguard tunnel, kill the device, the routes should drop automatically.
*/
function wg_stop($server)
{
if (does_interface_exist($server->interface)) {
legacy_interface_destroy($server->interface);
}
syslog(LOG_NOTICE, "Wireguard interface {$server->name} ({$server->interface}) stopped");
}
$opts = getopt('ah', [], $optind);
$args = array_slice($argv, $optind);
/* setup syslog logging */
openlog("wireguard", LOG_ODELAY, LOG_AUTH);
if (isset($opts['h']) || empty($args) || !in_array($args[0], ['start', 'stop', 'restart', 'configure'])) {
echo "Usage: wg-service-control.php [-a] [-h] [stop|start|restart|configure] [uuid]\n\n";
echo "\t-a all instances\n";
} elseif (isset($opts['a']) || !empty($args[1])) {
$server_id = $args[1] ?? null;
$action = $args[0];
$server_devs = [];
if (!empty((string)(new OPNsense\Wireguard\General())->enabled)) {
foreach ((new OPNsense\Wireguard\Server())->servers->server->iterateItems() as $key => $node) {
if (empty((string)$node->enabled)) {
continue;
}
if ($server_id != null && $key != $server_id) {
continue;
}
$server_devs[] = (string)$node->interface;
$statHandle = fopen($node->statFilename, "a+");
if (flock($statHandle, LOCK_EX)) {
switch ($action) {
case 'stop':
wg_stop($node);
break;
case 'start':
wg_start($node, $statHandle);
break;
case 'restart':
wg_stop($node);
wg_start($node, $statHandle);
break;
case 'configure':
if (
@md5_file($node->cnfFilename) != @file_get_contents($node->statFilename) ||
!does_interface_exist((string)$node->interface)
) {
wg_stop($node);
wg_start($node, $statHandle);
}
break;
}
flock($statHandle, LOCK_UN);
}
fclose($statHandle);
}
}
/**
* When -a is specified, cleaup up old or disabled instances (files and interfaces)
*/
if ($server_id == null) {
foreach (glob('/usr/local/etc/wireguard/wg*') as $filename) {
$this_dev = explode('.', basename($filename))[0];
if (!in_array($this_dev, $server_devs)) {
@unlink($filename);
if (does_interface_exist($this_dev)) {
legacy_interface_destroy($this_dev);
}
}
}
}
mwexecf('/usr/local/etc/rc.routing_configure');
}
closelog();
@@ -1,31 +1,36 @@
[start]
command:/usr/local/etc/rc.d/wireguard start; /usr/local/opnsense/scripts/OPNsense/Wireguard/post.sh
parameters:
command:/usr/local/opnsense/scripts/Wireguard/wg-service-control.php
parameters: start %s
type:script
message:Starting WireGuard
message: start wireguard instance %s
[stop]
command:/usr/local/etc/rc.d/wireguard stop
parameters:
command:/usr/local/opnsense/scripts/Wireguard/wg-service-control.php
parameters: stop %s
type:script
message:Stopping WireGuard
message: stop wireguard instance %s
[restart]
command:/usr/local/etc/rc.d/wireguard restart; /usr/local/opnsense/scripts/OPNsense/Wireguard/post.sh
parameters:
command:/usr/local/opnsense/scripts/Wireguard/wg-service-control.php
parameters: restart %s
type:script
message:Restarting WireGuard
description: Restart WireGuard
message: restart wireguard instance %s
[configure]
command:/usr/local/opnsense/scripts/Wireguard/wg-service-control.php
parameters: -a configure
type:script
message: configure wireguard instances
[renew]
command:/usr/local/opnsense/scripts/OPNsense/Wireguard/resolve-dns.bash
command:/usr/local/opnsense/scripts/Wireguard/reresolve-dns.py
parameters:
type:script
message:Renew DNS for WireGuard
description:Renew DNS for WireGuard on stale connections
[genkey]
command:/usr/local/opnsense/scripts/OPNsense/Wireguard/genkey.sh
command:/usr/local/opnsense/scripts/Wireguard/genkey.sh
parameters: %s
type:script_output
message:Generating WireGuard keys
@@ -0,0 +1,6 @@
###################################################################
# Local syslog-ng configuration filter definition [wireguard].
###################################################################
filter f_local_wireguard {
program("wireguard");
};
@@ -1,15 +1,2 @@
{% if helpers.exists('OPNsense.wireguard.general.enabled') and OPNsense.wireguard.general.enabled == '1' %}
wireguard_setup="/usr/local/opnsense/scripts/OPNsense/Wireguard/setup.sh"
wireguard_enable="YES"
{% if helpers.exists('OPNsense.wireguard.server.servers.server') %}
{% set activeservers=[] %}
{% for servers in helpers.toList('OPNsense.wireguard.server.servers.server') %}
{% if servers.enabled == '1' %}
{% do activeservers.append("wg" + servers.instance) %}
{% endif %}
{% endfor %}
{% endif %}
wireguard_interfaces="{{ activeservers | join(' ') }}"
{% else %}
# disable the wireguard rc scripts when installed, bootup handled via rc.syshook
wireguard_enable="NO"
{% endif %}

Some files were not shown because too many files have changed in this diff Show More