VPN / IPSec / Tunnel settings - work in progress for https://github.com/opnsense/core/issues/5279

This commit is contained in:
Ad Schellevis 2021-10-18 21:12:44 +02:00
parent fe727d2be1
commit dfe3932166
3 changed files with 262 additions and 0 deletions

View File

@ -0,0 +1,123 @@
<?php
/*
* Copyright (C) 2021 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\IPsec\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\Core\Backend;
use OPNsense\Core\Config;
/**
* Class TunnelController
* @package OPNsense\IPsec\Api
*/
class TunnelController extends ApiControllerBase
{
private function search($records)
{
$itemsPerPage = intval($this->request->getPost('rowCount', 'int', 9999));
$currentPage = intval($this->request->getPost('current', 'int', 1));
$offset = ($currentPage - 1) * $itemsPerPage;
$entry_keys = array_keys($records);
if ($this->request->hasPost('searchPhrase') && $this->request->getPost('searchPhrase') !== '') {
$searchPhrase = (string)$this->request->getPost('searchPhrase');
$entry_keys = array_filter($entry_keys, function ($key) use ($searchPhrase, $records) {
foreach ($records[$key] as $itemval) {
if (stripos($itemval, $searchPhrase) !== false) {
return true;
}
}
return false;
});
}
$formatted = array_map(function ($value) use (&$records) {
foreach ($records[$value] as $ekey => $evalue) {
$item[$ekey] = $evalue;
}
return $item;
}, array_slice($entry_keys, $offset, $itemsPerPage));
if ($this->request->hasPost('sort') && is_array($this->request->getPost('sort'))) {
$keys = array_keys($this->request->getPost('sort'));
$order = $this->request->getPost('sort')[$keys[0]];
$keys = array_column($formatted, $keys[0]);
array_multisort($keys, $order == 'asc' ? SORT_ASC : SORT_DESC, $formatted);
}
return [
'total' => count($entry_keys),
'rowCount' => $itemsPerPage,
'current' => $currentPage,
'rows' => $formatted,
];
}
public function searchPhase1Action()
{
$items = [];
$this->sessionClose();
$config = Config::getInstance()->object();
if (!empty($config->ipsec->phase1)) {
$idx = 0;
$ifs = [];
if ($config->interfaces->count() > 0) {
foreach ($config->interfaces->children() as $key => $node) {
$ifs[(string)$node->if] = !empty((string)$node->descr) ? (string)$node->descr : $key;
}
if ($config->virtualip->count() > 0) {
foreach ($config->virtualip->children() as $node) {
if (!empty((string)$node->vhid)) {
$key = (string)$node->interface . "_vip". (string)$node->vhid;
} else {
$key = (string)$node->subnet;
}
$ifs[$key] = "$node->subnet ({$node->descr})";
}
}
}
foreach ($config->ipsec->phase1 as $p1) {
$interface = (string)$p1->interface;
$ph1type= ['ikev1' => 'IKE', 'ikev2' => 'IKEv2', 'ike' => 'auto'];
$item = [
"id" => $idx,
"disabled" => !empty((string)$p1->disabled),
"protocol" => $p1->protocol == "inet" ? "IPv4" : "IPv6",
"iketype" => $ph1type[(string)$p1->iketype],
"interface" => !empty($ifs[$interface]) ? $ifs[$interface] : $interface,
"remote_gateway" => (string)$p1->{"remote-gateway"},
"mobile" => !empty((string)$p1->mobile),
"mode" => (string)$p1->mode,
"description" => (string)$p1->descr
];
$item['type'] = "{$item['protocol']} {$item['iketype']}";
$items[] = $item;
$idx++;
}
}
return $this->search($items);
}
}

View File

@ -0,0 +1,41 @@
<?php
/*
* Copyright (C) 2021 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\IPsec;
/**
* Class TunnelsController
* @package OPNsense\IPsec
*/
class TunnelsController extends \OPNsense\Base\IndexController
{
public function indexAction()
{
$this->view->pick('OPNsense/IPsec/tunnels');
}
}

View File

@ -0,0 +1,98 @@
<script>
$(function () {
const $grid = $('#grid-phase1').UIBootgrid({
search: '/api/ipsec/tunnel/search_phase1',
options: {
formatters: {
"commands": function (column, row) {
return '<button type="button" class="btn btn-xs btn-default command-edit bootgrid-tooltip" data-row-id="' + row.id + '"><span class="fa fa-fw fa-pencil"></span></button> ' +
'<button type="button" class="btn btn-xs btn-default command-copy bootgrid-tooltip" data-row-id="' + row.id + '"><span class="fa fa-fw fa-clone"></span></button>' +
'<button type="button" class="btn btn-xs btn-default command-delete bootgrid-tooltip" data-row-id="' + row.id + '"><span class="fa fa-fw fa-trash-o"></span></button>';
}
}
}
});
});
</script>
<div class="tab-content content-box col-xs-12 __mb">
<table id="grid-phase1" class="table table-condensed table-hover table-striped">
<thead>
<tr>
<th data-column-id="id" data-type="numeric" data-identifier="true" data-visible="false">ID</th>
<th data-column-id="type" data-type="string">{{ lang._('Type') }}</th>
<th data-column-id="remote_gateway" data-width="20em" data-type="string">{{ lang._('Remote Gateway') }}</th>
<th data-column-id="mode" data-width="20em" data-type="string">{{ lang._('Mode') }}</th>
<th data-column-id="proposal" data-type="string">{{ lang._('Phase 1 Proposal') }}</th>
<th data-column-id="authentication" data-type="string">{{ lang._('Authentication') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td colspan=7></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-primary">
<span class="fa fa-fw fa-plus"></span>
</button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default">
<span class="fa fa-fw fa-trash-o"></span>
</button>
</td>
</tr>
</tfoot>
</table>
</div>
<div class="tab-content content-box col-xs-12 __mb">
<table id="grid-phase2" class="table table-condensed table-hover table-striped">
<thead>
<tr>
<th data-column-id="id" data-type="numeric" data-identifier="true" data-visible="false">ID</th>
<th data-column-id="type" data-type="string">{{ lang._('Type') }}</th>
<th data-column-id="local_subnet" data-width="20em" data-type="string">{{ lang._('Local Subnet') }}</th>
<th data-column-id="remote_subnet" data-width="20em" data-type="string">{{ lang._('Remote Subnet') }}</th>
<th data-column-id="proposal" data-type="string">{{ lang._('Phase 2 Proposal') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td colspan=6></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-primary">
<span class="fa fa-fw fa-plus"></span>
</button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default">
<span class="fa fa-fw fa-trash-o"></span>
</button>
</td>
</tr>
</tfoot>
</table>
</div>
<div class="tab-content content-box col-xs-12 __mb">
<table class="table table-condensed">
<tbody>
<tr>
<td>
<input name="enable" type="checkbox" id="enable" value="yes" checked="checked"/>
<strong>{{ lang._('Enable IPsec') }}</strong>
</td>
</tr>
<tr>
<td>
<input type="submit" name="save" class="btn btn-primary" value="{{ lang._('Save') }}" />
</td>
</tr>
</tbody>
</table>
</div>