Zerotier Plugin (#211)

* Introduce Zerotier
* Add in services configuration.
* Add in Save callback to restart Zerotier Service
* Ensure interface is removed when deleting network.
This commit is contained in:
David Harrigan 2017-07-28 20:20:20 +01:00 committed by Franco Fichtner
parent 6c95a32340
commit 72a71eba03
14 changed files with 509 additions and 0 deletions

7
net/zerotier/Makefile Normal file
View file

@ -0,0 +1,7 @@
PLUGIN_NAME= zerotier
PLUGIN_VERSION= 0.1.0
PLUGIN_COMMENT= Network virtualization everywhere
PLUGIN_DEPENDS= zerotier
PLUGIN_MAINTAINER= dharrigan@gmail.com
.include "../../Mk/plugins.mk"

6
net/zerotier/pkg-descr Normal file
View file

@ -0,0 +1,6 @@
ZeroTier can be used for on-premise network virtualization, as a
peer to peer VPN for mobile teams, for hybrid or multi-data-center
cloud deployments, or just about anywhere else secure software
defined virtual networking is useful.
WWW: https://www.zerotier.com

View file

@ -0,0 +1,44 @@
<?php
/**
* Copyright (C) 2017 David Harrigan
* Copyright (C) 2017 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.
*
*/
function zerotier_services()
{
$services = array();
$services[] = array(
'description' => gettext('Zerotier'),
'configd' => array(
'restart' => array('zerotier restart'),
'start' => array('zerotier start'),
'stop' => array('zerotier stop'),
),
'name' => 'zerotier',
'pidfile' => '/var/db/zerotier-one/zerotier-one.pid'
);
return $services;
}

View file

@ -0,0 +1,193 @@
<?php
/**
* Copyright (C) 2017 David Harrigan
* Copyright (C) 2017 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\Zerotier\Api;
use \OPNsense\Core\Config;
use \OPNsense\Base\ApiMutableModelControllerBase;
use \OPNsense\Core\Backend;
use \OPNsense\Base\UIModelGrid;
use \OPNsense\Zerotier\Zerotier;
class ZerotierController extends ApiMutableModelControllerBase
{
static protected $internalModelName = 'Zerotier';
static protected $internalModelClass = '\OPNsense\Zerotier\Zerotier';
public function getAction()
{
$result = array();
if ($this->request->isGet()) {
$mdlZerotier = new Zerotier();
$result['zerotier'] = $mdlZerotier->getNodes();
}
return $result;
}
public function searchNetworkAction()
{
$this->sessionClose();
$mdlZerotier = $this->getModel();
$grid = new UIModelGrid($mdlZerotier->networks->network);
return $grid->fetchBindRequest(
$this->request,
array("enabled", "networkId", "description")
);
}
public function getNetworkAction($uuid = null)
{
$mdlZerotier = $this->getModel();
if ($uuid != null) {
$network = $mdlZerotier->getNodeByReference('networks.network.' . $uuid);
if ($network != null) {
return array("network" => $network->getNodes());
}
} else {
$network = $mdlZerotier->networks->network->add();
return array("network" => $network->getNodes());
}
return array();
}
public function setNetworkAction()
{
$result = array("result" => "failed");
if ($this->request->isPost()) {
$mdlZerotier = new Zerotier();
$mdlZerotier->setNodes($this->request->getPost("network"));
$validationMessages = $mdlZerotier->performValidation();
foreach ($validationMessages as $field => $msg) {
if(!array_key_exists("validation", $result)) {
$result["validations"] = array();
}
$result["validation"]["network.".$msg->getField()] = $msg->getMessage();
}
if($validationMessages->count() == 0) {
unset($result["validations"]);
$mdlZerotier->serializeToConfig();
Config::getInstance()->save();
$result["result"] = "saved";
}
}
return $result;
}
public function addNetworkAction()
{
$result = array("result" => "failed");
if ($this->request->isPost() && $this->request->hasPost("network")) {
$result = array("result" => "failed", "validations" => array());
$mdlZerotier = $this->getModel();
$network = $mdlZerotier->networks->network->add();
$network->setNodes($this->request->getPost("network"));
$validationMessages = $mdlZerotier->performValidation();
foreach ($validationMessages as $field => $msg) {
$fieldName = str_replace($network->__reference, "network", $msg->getField());
$result["validations"][$fieldName] = $msg->getMessage();
}
if ($validationMessages->count() == 0) {
unset($result["validations"]);
$mdlZerotier->serializeToConfig();
Config::getInstance()->save();
$result["result"] = "saved";
}
}
return $result;
}
public function delNetworkAction($uuid = null)
{
$result = array("result" => "failed");
if ($this->request->isPost()) {
$mdlZerotier = $this->getModel();
if ($uuid != null) {
$node = $mdlZerotier->getNodeByReference('networks.network.' . $uuid);
if ($node->enabled->__toString() == "1") {
# Ensure we remove the interface before deleting the network
$this->toggleZerotierNetwork($node->networkId, 0);
}
if ($mdlZerotier->networks->network->del($uuid)) {
$mdlZerotier->serializeToConfig();
Config::getInstance()->save();
$result["result"] = "deleted";
} else {
$result["result"] = "not found";
}
}
}
return $result;
}
public function toggleNetworkAction($uuid)
{
$result = array("result" => "failed");
if ($this->request->isPost()) {
$mdlZerotier = $this->getModel();
if ($uuid != null) {
$node = $mdlZerotier->getNodeByReference('networks.network.' . $uuid);
if ($node != null) {
$networkId = $node->networkId;
if ($node->enabled->__toString() == "1") {
$node->enabled = "0";
$result['result'] = $this->toggleZerotierNetwork($networkId, 0);
} else {
$node->enabled = "1";
$result['result'] = $this->toggleZerotierNetwork($networkId, 1);
}
$mdlZerotier->serializeToConfig();
Config::getInstance()->save();
}
}
}
return $result;
}
public function reconfigureZerotierAction()
{
if ($this->request->isPost()) {
$this->sessionClose();
$backend = new Backend();
$backend->configdRun("template reload OPNsense/zerotier");
$result = trim($backend->configdRun("zerotier restart"));
return array("status" => $result);
} else {
return array("status" => "failed");
}
}
private function toggleZerotierNetwork($networkId, $enabled)
{
$backend = new Backend();
return trim($backend->configdRun("zerotier ".($enabled ? "join " : "leave ").$networkId));
}
}

View file

@ -0,0 +1,40 @@
<?php
/**
* Copyright (C) 2017 David Harrigan
* Copyright (C) 2017 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\Zerotier;
class IndexController extends \OPNsense\Base\IndexController
{
public function indexAction()
{
$this->view->title = "VPN: Zerotier";
$this->view->pick('OPNsense/Zerotier/index');
$this->view->formDialogNetwork = $this->getForm("dialogNetwork");
}
}

View file

@ -0,0 +1,14 @@
<form>
<field>
<id>network.networkId</id>
<label>Network ID</label>
<type>text</type>
<help>Zerotier Network ID e.g., 161411d4f6abe4a7</help>
</field>
<field>
<id>network.description</id>
<label>Description</label>
<type>text</type>
<help>Description to help identify this network</help>
</field>
</form>

View file

@ -0,0 +1,9 @@
<acl>
<page-vpn-zerotier>
<name>VPN: Zerotier</name>
<patterns>
<pattern>ui/zerotier/*</pattern>
<pattern>api/zerotier/*</pattern>
</patterns>
</page-vpn-zerotier>
</acl>

View file

@ -0,0 +1,5 @@
<menu>
<VPN>
<Zerotier order="30" VisibleName="Zerotier" cssClass="fa fa-lock fa-fw" url="/ui/zerotier/"/>
</VPN>
</menu>

View file

@ -0,0 +1,37 @@
<?php
/**
* Copyright (C) 2017 David Harrigan
* Copyright (C) 2017 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\Zerotier;
use OPNsense\Base\BaseModel;
class Zerotier extends BaseModel
{
}

View file

@ -0,0 +1,24 @@
<model>
<mount>//OPNsense/zerotier</mount>
<description>
Zerotier - Virtual Networks That Just Work.
</description>
<items>
<networks>
<network type="ArrayField">
<enabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
</enabled>
<networkId type="TextField">
<default></default>
<Required>Y</Required>
</networkId>
<description type="TextField">
<default></default>
<Required>N</Required>
</description>
</network>
</networks>
</items>
</model>

View file

@ -0,0 +1,99 @@
{#
OPNsense® is Copyright © 2014 2017 by Deciso B.V.
Copyright (C) 2017 David Harrigan
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.
#}
<script type="text/javascript">
$(document).ready(function() {
$("#grid-networks").UIBootgrid(
{
search: '/api/zerotier/zerotier/searchNetwork',
get:'/api/zerotier/zerotier/getNetwork/',
set:'/api/zerotier/zerotier/setNetwork/',
add:'/api/zerotier/zerotier/addNetwork/',
del:'/api/zerotier/zerotier/delNetwork/',
toggle:'/api/zerotier/zerotier/toggleNetwork/'
}
);
$("#reconfigureZerotier").click(function() {
$("#reconfigureZerotierProgress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url="/api/zerotier/zerotier/reconfigureZerotier", sendData={}, callback=function(data, status) {
$("#reconfigureZerotierProgress").removeClass("fa fa-spinner fa-pulse");
if (status != "success" || data['status'] != 'OK') {
BootstrapDialog.show({
type: BootstrapDialog.TYPE_WARNING,
title: "{{ lang._('Error reconfiguring Zerotier') }}",
message: data['status'],
draggable: true
});
}
});
});
});
</script>
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
<li class="active"><a data-toggle="tab" href="#networks">{{ lang._('Networks') }}</a></li>
</ul>
<div class="tab-content content-box tab-content">
<div id="networks" class="tab-pane fade in active">
<table id="grid-networks" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="dialogNetwork">
<thead>
<tr>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="networkId" data-type="string" data-visible="true">{{ lang._('Network Id') }}</th>
<th data-column-id="description" data-width="7em" data-type="string" data-visible="true">{{ lang._('Description') }}</th>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
</div>
<div class="col-md-12">
<hr/>
<button class="btn btn-primary" id="reconfigureZerotier" type="button"><b>{{ lang._('Save') }}</b> <i id="reconfigureZerotierProgress" class=""></i></button>
<br/><br/>
</div>
</div>
{{ partial("layout_partials/base_dialog",['fields':formDialogNetwork,'id':'dialogNetwork','label':lang._('Edit Zerotier Network')]) }}

View file

@ -0,0 +1,29 @@
[start]
command:/usr/local/etc/rc.d/zerotier start
parameters:
type:script
message:Starting Zerotier Service
[stop]
command:/usr/local/etc/rc.d/zerotier stop
parameters:
type:script
message:Stopping Zerotier Service
[restart]
command:/usr/local/etc/rc.d/zerotier restart
parameters:
type:script
message:Restarting Zerotier Service
[join]
command:/usr/local/bin/zerotier-cli
parameters: join %s
type:script_output
message:Joining Zerotier Network
[leave]
command:/usr/local/bin/zerotier-cli
parameters: leave %s
type:script_output
message:Leaving Zerotier Network

View file

@ -0,0 +1 @@
zerotier:/etc/rc.conf.d/zerotier

View file

@ -0,0 +1 @@
zerotier_enable="YES"