Add ftp-proxy plugin (#36)

This commit is contained in:
fbrendel 2016-09-27 09:08:57 +02:00 committed by Franco Fichtner
parent 6f63e214ae
commit e79baf0c30
18 changed files with 1060 additions and 0 deletions

View file

@ -0,0 +1,28 @@
DIFF='--- filter.inc.ftpproxy 2016-09-21 16:38:53.947075272 +0200
+++ filter.inc.orig 2016-09-21 16:47:29.239370565 +0200
@@ -1450,7 +1450,6 @@
$natrules = "no nat proto carp\n";
$natrules .= "no rdr proto carp\n";
- $natrules .= "nat-anchor \"ftp-proxy/*\"\n";
$natrules .= "nat-anchor \"natearly/*\"\n";
$natrules .= "nat-anchor \"natrules/*\"\n\n";
@@ -1703,7 +1702,6 @@
unset($tonathosts, $tonathosts_array, $numberofnathosts);
}
- $natrules .= "rdr-anchor \"ftp-proxy/*\"\n";
$natrules .= "\n# Load balancing\n";
$natrules .= "rdr-anchor \"relayd/*\"\n";
@@ -2482,7 +2480,6 @@
$ipfrules = "";
- $ipfrules .= "anchor \"ftp-proxy/*\"\n";
/* relayd */
$ipfrules .= "anchor \"relayd/*\"\n";
/* OpenVPN user rules from radius */'
echo "$DIFF" | patch -b -p1 /usr/local/etc/inc/filter.inc

View file

@ -0,0 +1,28 @@
DIFF='--- filter.inc.orig 2016-09-21 16:39:02.853045967 +0200
+++ filter.inc.ftpproxy 2016-09-21 16:38:53.947075272 +0200
@@ -1450,6 +1450,7 @@
$natrules = "no nat proto carp\n";
$natrules .= "no rdr proto carp\n";
+ $natrules .= "nat-anchor \"ftp-proxy/*\"\n";
$natrules .= "nat-anchor \"natearly/*\"\n";
$natrules .= "nat-anchor \"natrules/*\"\n\n";
@@ -1702,6 +1703,7 @@
unset($tonathosts, $tonathosts_array, $numberofnathosts);
}
+ $natrules .= "rdr-anchor \"ftp-proxy/*\"\n";
$natrules .= "\n# Load balancing\n";
$natrules .= "rdr-anchor \"relayd/*\"\n";
@@ -2480,6 +2482,7 @@
$ipfrules = "";
+ $ipfrules .= "anchor \"ftp-proxy/*\"\n";
/* relayd */
$ipfrules .= "anchor \"relayd/*\"\n";
/* OpenVPN user rules from radius */'
echo "$DIFF" | patch -b -p1 /usr/local/etc/inc/filter.inc

7
net/ftp-proxy/Makefile Normal file
View file

@ -0,0 +1,7 @@
PLUGIN_NAME= ftp-proxy
PLUGIN_VERSION= 0.1
PLUGIN_COMMENT= Control ftp-proxy processes
PLUGIN_MAINTAINER= frank.brendel@eurolog.com
PLUGIN_PRIVATE= yes
.include "../../Mk/plugins.mk"

View file

@ -0,0 +1,42 @@
<?php
/**
* Copyright (C) 2016 EURO-LOG AG
*
* 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\FtpProxy\Api;
use \OPNsense\Base\ApiControllerBase;
use \OPNsense\FtpProxy\FtpProxy;
/**
* Class ServiceController
* @package OPNsense\FtpProxy
*/
class ServiceController extends ApiControllerBase
{
}

View file

@ -0,0 +1,305 @@
<?php
/**
* Copyright (C) 2016 EURO-LOG AG
*
* 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\FtpProxy\Api;
use \OPNsense\Base\ApiControllerBase;
use \OPNsense\Core\Config;
use \OPNsense\Core\Backend;
use \OPNsense\FtpProxy\FtpProxy;
use \OPNsense\Base\UIModelGrid;
/**
* Class SettingsController
* @package OPNsense\FtpProxy
*/
class SettingsController extends ApiControllerBase
{
/**
* retrieve ftpproxy settings or return defaults
* @param $uuid item unique id
* @return array
*/
public function getProxyAction($uuid = null)
{
$mdlFtpProxy = new FtpProxy();
if ($uuid != null) {
$node = $mdlFtpProxy->getNodeByReference('ftpproxies.ftpproxy.' . $uuid);
if ($node != null) {
// return node
return array("ftpproxy" => $node->getNodes());
}
} else {
// generate new node, but don't save to disc
$node = $mdlFtpProxy->ftpproxies->ftpproxy->Add();
return array("ftpproxy" => $node->getNodes());
}
return array();
}
/**
* update ftpproxy with given properties
* @param $uuid item unique id
* @return array
*/
public function setProxyAction($uuid)
{
if ($this->request->isPost() && $this->request->hasPost("ftpproxy")) {
$mdlFtpProxy = new FtpProxy();
// keep a list to detect duplicates later
$CurrentProxies = $mdlFtpProxy->getNodes();
if ($uuid != null) {
$node = $mdlFtpProxy->getNodeByReference('ftpproxies.ftpproxy.' . $uuid);
if ($node != null) {
$Enabled = $node->enabled->__toString();
// get current ftp-proxy flags for stopping it later
$OldFlags = $mdlFtpProxy->configToFlags($node);
$result = array("result" => "failed", "validations" => array());
$proxyInfo = $this->request->getPost("ftpproxy");
$node->setNodes($proxyInfo);
$valMsgs = $mdlFtpProxy->performValidation();
foreach ($valMsgs as $field => $msg) {
$fieldnm = str_replace($node->__reference, "ftpproxy", $msg->getField());
$result["validations"][$fieldnm] = $msg->getMessage();
}
if (count($result['validations']) == 0) {
// check for duplicates
foreach ($CurrentProxies['ftpproxies']['ftpproxy'] as $CurrentUUID => &$CurrentProxy) {
if ($node->listenaddress->__toString() == $CurrentProxy['listenaddress'] &&
$node->listenport->__toString() == $CurrentProxy['listenport'] &&
$uuid != $CurrentUUID) {
return array(
"result" => "failed",
"validations" => array(
"ftpproxy.listenaddress" => "Listen address in combination with Listen port already exists.",
"ftpproxy.listenport" => "Listen port in combination with Listen address already exists."
)
);
}
}
// retrieve ftp-proxy flags and set defaults
$NewFlags = $mdlFtpProxy->configToFlags($node);
// save config if validated correctly
$mdlFtpProxy->serializeToConfig();
Config::getInstance()->save();
$backend = new Backend();
// apply new settings to the ftp-proxy process
// stop ftp-proxy with old flags
if ($Enabled == 1) {
$backend->configdpRun('ftpproxy stop ', array($OldFlags));
}
$node = $mdlFtpProxy->getNodeByReference('ftpproxies.ftpproxy.' . $uuid);
// start ftp-proxy with new flags
if ($node != null && $node->enabled->__toString() == 1) {
$backend->configdpRun('ftpproxy start ', array($NewFlags));
}
// make the changes boot resistant in /etc/rc.conf.d/ftpproxy
$backend->configdRun("template reload OPNsense.FtpProxy");
$result = array("result" => "saved");
}
return $result;
}
}
}
return array("result" => "failed");
}
/**
* add new ftpproxy and set with attributes from post
* @return array
*/
public function addProxyAction()
{
$result = array("result" => "failed");
if ($this->request->isPost() && $this->request->hasPost("ftpproxy")) {
$result = array("result" => "failed", "validations" => array());
$mdlFtpProxy = new FtpProxy();
// keep a list to detect duplicates later
$CurrentProxies = $mdlFtpProxy->getNodes();
$node = $mdlFtpProxy->ftpproxies->ftpproxy->Add();
$node->setNodes($this->request->getPost("ftpproxy"));
$valMsgs = $mdlFtpProxy->performValidation();
foreach ($valMsgs as $field => $msg) {
$fieldnm = str_replace($node->__reference, "ftpproxy", $msg->getField());
$result["validations"][$fieldnm] = $msg->getMessage();
}
if (count($result['validations']) == 0) {
foreach ($CurrentProxies['ftpproxies']['ftpproxy'] as &$CurrentProxy) {
if ($node->listenaddress->__toString() == $CurrentProxy['listenaddress']
&& $node->listenport->__toString() == $CurrentProxy['listenport']) {
return array(
"result" => "failed",
"validations" => array(
"ftpproxy.listenaddress" => "Listen address in combination with Listen port already exists.",
"ftpproxy.listenport" => "Listen port in combination with Listen address already exists."
)
);
}
}
// retrieve ftp-proxy flags and set defaults
$Flags = $mdlFtpProxy->configToFlags($node);
// save config if validated correctly
$mdlFtpProxy->serializeToConfig();
Config::getInstance()->save();
if ($node->enabled->__toString() == 1) {
$backend = new Backend();
$backend->configdpRun('ftpproxy start ', array($Flags));
// add it to /etc/rc.conf.d/ftpproxy
$backend->configdRun("template reload OPNsense.FtpProxy");
}
$result = array("result" => "saved");
}
return $result;
}
return $result;
}
/**
* delete ftpproxy by uuid
* @param $uuid item unique id
* @return array status
*/
public function delProxyAction($uuid)
{
$result = array("result" => "failed");
if ($this->request->isPost()) {
$mdlFtpProxy = new FtpProxy();
if ($uuid != null) {
$node = $mdlFtpProxy->getNodeByReference('ftpproxies.ftpproxy.' . $uuid);
if ($node != null) {
$backend = new Backend();
// stop if the ftp-proxy is running
if ($node->enabled->__toString() == 1) {
$backend->configdpRun('ftpproxy stop ', array($mdlFtpProxy->configToFlags($node)));
}
if ($mdlFtpProxy->ftpproxies->ftpproxy->del($uuid) == true) {
// if item is removed, serialize to config and save
$mdlFtpProxy->serializeToConfig();
Config::getInstance()->save();
$result['result'] = 'deleted';
// remove it from /etc/rc.conf.d/ftpproxy
$backend->configdRun("template reload OPNsense.FtpProxy");
}
} else {
$result['result'] = 'not found';
}
}
}
return $result;
}
/**
* toggle ftpproxy by uuid (enable/disable)
* @param $uuid item unique id
* @return array status
*/
public function toggleProxyAction($uuid)
{
$result = array("result" => "failed");
if ($this->request->isPost()) {
$mdlFtpProxy = new FtpProxy();
if ($uuid != null) {
$node = $mdlFtpProxy->getNodeByReference('ftpproxies.ftpproxy.' . $uuid);
if ($node != null) {
$backend = new Backend();
if ($node->enabled->__toString() == "1") {
$result['result'] = "Disabled";
$node->enabled = "0";
$response = $backend->configdpRun('ftpproxy stop ', array($mdlFtpProxy->configToFlags($node)));
} else {
$result['result'] = "Enabled";
$node->enabled = "1";
$response = $backend->configdpRun('ftpproxy start ', array($mdlFtpProxy->configToFlags($node)));
}
// if item has toggled, serialize to config and save
$mdlFtpProxy->serializeToConfig();
Config::getInstance()->save();
$backend->configdRun("template reload OPNsense.FtpProxy");
}
}
}
return $result;
}
/**
*
* search ftpproxy
* @return array
*/
public function searchProxyAction()
{
$this->sessionClose();
$fields = array(
"enabled",
"listenaddress",
"listenport",
"sourceaddress",
"rewritesourceport",
"idletimeout",
"maxsessions",
"reverseaddress",
"reverseport",
"logconnections",
"debuglevel",
"description"
);
$mdlFtpProxy = new FtpProxy();
$grid = new UIModelGrid($mdlFtpProxy->ftpproxies->ftpproxy);
$response = $grid->fetchBindRequest(
$this->request,
$fields,
"listenport"
);
$backend = new Backend();
foreach($response['rows'] as &$row) {
$node = $mdlFtpProxy->getNodeByReference('ftpproxies.ftpproxy.' . $row['uuid']);
$status = trim($backend->configdpRun('ftpproxy status ', array($mdlFtpProxy->configToFlags($node))));
if ($status == 'OK') {
$row['status'] = 0;
continue;
}
$row['status'] = 2;
}
return $response;
}
}

View file

@ -0,0 +1,50 @@
<?php
/**
* Copyright (C) 2016 EURO-LOG AG
*
* 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\FtpProxy;
/**
* Class IndexController
* @package OPNsense\FtpProxy
*/
class IndexController extends \OPNsense\Base\IndexController
{
/**
* ftpproxy index page
* @throws \Exception
*/
public function indexAction()
{
$this->view->title = gettext('FTP Proxy Server');
// include dialog form definitions
$this->view->formDialogEdit = $this->getForm("dialogEdit");
$this->view->pick('OPNsense/FtpProxy/index');
}
}

View file

@ -0,0 +1,40 @@
<?php
/**
* Copyright (C) 2016 EURO-LOG AG
*
* 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\FtpProxy;
/**
* Class ItemController
* @package OPNsense\FtpProxy
*/
class ItemController extends \OPNsense\Base\IndexController
{
}

View file

@ -0,0 +1,74 @@
<form>
<field>
<id>ftpproxy.enabled</id>
<label>enabled</label>
<type>checkbox</type>
<help><![CDATA[Enable or disable this ftp proxy.]]></help>
</field>
<field>
<id>ftpproxy.listenaddress</id>
<label>Listen address</label>
<type>text</type>
<help><![CDATA[Address where the proxy will listen for redirected control connections. The default is 127.0.0.1.]]></help>
</field>
<field>
<id>ftpproxy.listenport</id>
<label>Listen port</label>
<type>text</type>
<help><![CDATA[Port where the proxy will listen for redirected connections. The default is port 8021.]]></help>
</field>
<field>
<id>ftpproxy.sourceaddress</id>
<label>Source address</label>
<type>text</type>
<help><![CDATA[The proxy will use this as the source address for the control connection to a server.]]></help>
</field>
<field>
<id>ftpproxy.rewritesourceport</id>
<label>Rewrite source port</label>
<type>checkbox</type>
<help><![CDATA[Rewrite sourceport to 20 in active mode to suit ancient clients that insist on this RFC property.]]></help>
</field>
<field>
<id>ftpproxy.idletimeout</id>
<label>Idle timeout</label>
<type>text</type>
<help><![CDATA[Number of seconds that the control connection can be idle, before the proxy will disconnect. The maximum is 86400 seconds, which is also the default. Do not set this too low, because the control connection is usually idle when large data transfers are taking place.]]></help>
</field>
<field>
<id>ftpproxy.maxsessions</id>
<label>Max sessions</label>
<type>text</type>
<help><![CDATA[Maximum number of concurrent FTP sessions. When the proxy reaches this limit, new connections are denied. The default is 100 sessions. The limit can be lowered to a minimum of 1, or raised to a maximum of 500.]]></help>
</field>
<field>
<id>ftpproxy.reverseaddress</id>
<label>Reverse address</label>
<type>text</type>
<help><![CDATA[Fixed server address, also known as reverse mode. The proxy will always connect to the same server, regardless of where the client wanted to connect to (before it was redirected). Use this option to proxy for a server behind NAT, or to forward all connections to another proxy.]]></help>
</field>
<field>
<id>ftpproxy.reverseport</id>
<label>Reverse port</label>
<type>text</type>
<help><![CDATA[Fixed server port. Only used in combination with <i>Reverse address</i>. The default is port 21.]]></help>
</field>
<field>
<id>ftpproxy.logconnections</id>
<label>Log connections</label>
<type>checkbox</type>
<help><![CDATA[Set the 'log' flag on pf rules committed by ftp-proxy. The pf rules do not log by default.]]></help>
</field>
<field>
<id>ftpproxy.debuglevel</id>
<label>Debug level</label>
<type>text</type>
<help><![CDATA[Debug level, ranging from 0 to 7. Higher is more verbose. The default is 5. (These levels correspond to the syslog(3) levels.)]]></help>
</field>
<field>
<id>ftpproxy.description</id>
<label>Description</label>
<type>text</type>
<help><![CDATA[Briefly description of this ftp proxy]]></help>
</field>
</form>

View file

@ -0,0 +1,10 @@
<acl>
<page-services-ftpproxy>
<name>WebCfg - Services: Ftp Proxy page</name>
<description>Allow access to the 'Services: Ftp Proxy' page.</description>
<patterns>
<pattern>ui/ftpproxy/*</pattern>
<pattern>api/ftpproxy/*</pattern>
</patterns>
</page-services-ftpproxy>
</acl>

View file

@ -0,0 +1,89 @@
<?php
/**
* Copyright (C) 2016 EURO-LOG AG
*
* 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\FtpProxy;
use OPNsense\Base\BaseModel;
/**
* Class FtpProxy
* @package OPNsense\FtpProxy
*/
class FtpProxy extends BaseModel
{
/**
* map config to ftp-proxy flags
* and set default values
* @param $node configuration
* @return string
*/
public function configToFlags($node)
{
$flags = ' -b ' . $node->listenaddress->__toString();
$flags .= ' -p ' . $node->listenport->__toString();
if ($node->sourceaddress->__toString() != "") {
$flags .= ' -a ' . $node->sourceaddress->__toString();
}
if ($node->rewritesourceport->__toString() == 1) {
$flags .= ' -r ';
}
if ($node->idletimeout->__toString() == "") {
$node->__set('idletimeout', 86400);
}
if ($node->idletimeout->__toString() != 86400) {
$flags .= ' -t ' . $node->idletimeout->__toString();
}
if ($node->maxsessions->__toString() == "") {
$node->__set('maxsessions', 100);
}
if ($node->maxsessions->__toString() != 100) {
$flags .= ' -m ' . $node->maxsessions->__toString();
}
if ($node->reverseaddress->__toString() != "") {
$flags .= ' -R ' . $node->reverseaddress->__toString();
}
if ($node->reverseport->__toString() == "") {
$node->__set('reverseport', 21);
}
if ($node->reverseport->__toString() != 21) {
$flags .= ' -P ' . $node->reverseport->__toString();
}
if ($node->logconnections->__toString() == 1) {
$flags .= ' -v ';
}
if ($node->debuglevel->__toString() == "") {
$node->__set('debuglevel', 5);
}
if ($node->debuglevel->__toString() != 5) {
$flags .= ' -D ' . $node->debuglevel->__toString();
}
return $flags;
}
}

View file

@ -0,0 +1,78 @@
<model>
<mount>//OPNsense/ftpproxy</mount>
<description>Ftp Proxy settings</description>
<items>
<ftpproxies>
<ftpproxy type="ArrayField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<listenaddress type="TextField">
<Required>Y</Required>
<default>127.0.0.1</default>
<mask>/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-4]|2[0-5][0-9]|[01]?[0-9][0-9]?)$/</mask>
<ValidationMessage>Listen address must be a valid IPv4 address</ValidationMessage>
</listenaddress>
<listenport type="IntegerField">
<default>8021</default>
<Required>Y</Required>
<MinimumValue>1</MinimumValue>
<MaximumValue>65535</MaximumValue>
<ValidationMessage>Listen port needs to be an integer value between 1 and 65535</ValidationMessage>
</listenport>
<sourceaddress type="TextField">
<Required>N</Required>
<mask>/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-4]|2[0-5][0-9]|[01]?[0-9][0-9]?)$/</mask>
<ValidationMessage>Source address must be a valid IPv4 address</ValidationMessage>
</sourceaddress>
<rewritesourceport type="BooleanField">
<default>0</default>
<Required>N</Required>
</rewritesourceport>
<idletimeout type="IntegerField">
<default>86400</default>
<Required>N</Required>
<MinimumValue>1</MinimumValue>
<MaximumValue>86400</MaximumValue>
<ValidationMessage>Idle timeout needs to be an integer value between 1 and 86400</ValidationMessage>
</idletimeout>
<maxsessions type="IntegerField">
<default>100</default>
<Required>N</Required>
<MinimumValue>1</MinimumValue>
<MaximumValue>500</MaximumValue>
<ValidationMessage>Maximum number of concurrent FTP sessions needs to be an integer value between 1 and 500</ValidationMessage>
</maxsessions>
<reverseaddress type="TextField">
<Required>N</Required>
<mask>/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-4]|2[0-5][0-9]|[01]?[0-9][0-9]?)$/</mask>
<ValidationMessage>Reverse address must be a valid IPv4 address</ValidationMessage>
</reverseaddress>
<reverseport type="IntegerField">
<default>21</default>
<Required>N</Required>
<MinimumValue>1</MinimumValue>
<MaximumValue>65535</MaximumValue>
<ValidationMessage>Reverse port needs to be an integer value between 1 and 65535</ValidationMessage>
</reverseport>
<logconnections type="BooleanField">
<default>0</default>
<Required>N</Required>
</logconnections>
<debuglevel type="IntegerField">
<default>5</default>
<Required>N</Required>
<MinimumValue>0</MinimumValue>
<MaximumValue>7</MaximumValue>
<ValidationMessage>Debug level needs to be an integer value between 0 and 7</ValidationMessage>
</debuglevel>
<description type="TextField">
<Required>N</Required>
<mask>/^([\t\n\v\f\r 0-9a-zA-Z.,_\x{00A0}-\x{FFFF}]){1,255}$/u</mask>
<ValidationMessage>Enter a description.</ValidationMessage>
</description>
</ftpproxy>
</ftpproxies>
</items>
</model>

View file

@ -0,0 +1,5 @@
<menu>
<Services>
<FtpProxy VisibleName="Ftp Proxy Server" cssClass="fa fa-bolt fa-fw" order="11" url="/ui/ftpproxy/"/>
</Services>
</menu>

View file

@ -0,0 +1,115 @@
{#
Copyright © 2016 by EURO-LOG AG
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() {
/**
* inline open dialog, go back to previous page on exit
*/
function openDialog(uuid) {
var editDlg = "DialogEdit";
var setUrl = "/api/ftpproxy/settings/setProxy/";
var getUrl = "/api/ftpproxy/settings/getProxy/";
var urlMap = {};
urlMap['frm_' + editDlg] = getUrl + uuid;
mapDataToFormUI(urlMap).done(function () {
// update selectors
$('.selectpicker').selectpicker('refresh');
// clear validation errors (if any)
clearFormValidation('frm_' + editDlg);
// show
$('#'+editDlg).modal({backdrop: 'static', keyboard: false});
$('#'+editDlg).on('hidden.bs.modal', function () {
// go back to previous page on exit
parent.history.back();
});
});
}
/*************************************************************************************************************
* link grid actions
*************************************************************************************************************/
$("#grid-proxies").UIBootgrid(
{ 'search':'/api/ftpproxy/settings/searchProxy',
'get':'/api/ftpproxy/settings/getProxy/',
'set':'/api/ftpproxy/settings/setProxy/',
'add':'/api/ftpproxy/settings/addProxy/',
'del':'/api/ftpproxy/settings/delProxy/',
'toggle':'/api/ftpproxy/settings/toggleProxy/'
}
);
{% if (selected_uuid|default("") != "") %}
openDialog(uuid='{{selected_uuid}}');
{% endif %}
/*************************************************************************************************************
* Commands
*************************************************************************************************************/
});
</script>
<!-- <ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
<li class="active"><a data-toggle="tab" href="#grid-proxies">{{ lang._('FTP Proxies') }}</a></li>
</ul> -->
<div class="tab-content content-box tab-content">
<div id="ftpproxies" class="tab-pane fade in active">
<!-- tab page "ftpproxy items" -->
<table id="grid-proxies" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogEdit">
<thead>
<tr>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="listenaddress" data-type="string" data-visible="true">{{ lang._('Listen Address') }}</th>
<th data-column-id="listenport" data-type="string" data-visible="true">{{ lang._('Listen Port') }}</th>
<th data-column-id="sourceaddress" data-type="string" data-visible="true">{{ lang._('Source Address') }}</th>
<th data-column-id="description" data-type="string">{{ lang._('Description') }}</th>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</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></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>
{# include dialog #}
{{ partial("layout_partials/base_dialog",['fields':formDialogEdit,'id':'DialogEdit','label':'Edit Proxy'])}}

View file

@ -0,0 +1,120 @@
#!/bin/sh
#
# Copyright (C) 2016 EURO-LOG AG
#
# 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.
#
ACTION=$1
shift
FLAGS=$@
# determine listenaddress and listenport to identify ftp-proxy process
for FLAG in $FLAGS; do
if [ "$FLAG" == "-b" ]; then
NEXT_FLAG="LISTENADDRESS"
continue
fi
if [ "$FLAG" == "-p" ]; then
NEXT_FLAG="LISTENPORT"
continue
fi
if [ "X$NEXT_FLAG" != "X" ]; then
if [ "$NEXT_FLAG" == "LISTENADDRESS" -a "X$FLAG" != "X" ]; then
LISTENADDRESS=$FLAG
NEXT_FLAG=""
fi
if [ "$NEXT_FLAG" == "LISTENPORT" -a "X$FLAG" != "X" ]; then
LISTENPORT=$FLAG
NEXT_FLAG=""
fi
fi
if [ "X$LISTENADDRESS" != "X" -a "X$LISTENPORT" != "X" ]; then
break
fi
done
if [ "X$LISTENADDRESS" == "X" -o "X$LISTENPORT" == "X" ]; then
( >&2 echo "Either listenaddress or listenport not given. Check -b and -p flags." )
exit 999
fi
ftpproxy_start () {
ftpproxy_status
if [ $? -gt 0 ]; then # already running
return 0
fi
/usr/sbin/ftp-proxy $FLAGS
return $?
}
ftpproxy_stop () {
ftpproxy_status
PID=$?
if [ $PID -eq 0 ]; then # already stopped
return 0
fi
kill $PID
return $?
}
ftpproxy_restart () {
ftpproxy_stop
if [ $? -ne 0 ]; then
return $?
fi
ftpproxy_start
return $?
}
ftpproxy_status () {
PID=`ps ax -o pid= -o command= | grep "/usr/sbin/ftp-proxy -b $LISTENADDRESS -p $LISTENPORT" | grep -v grep | awk '{ print $1 }'`
if [ "X$PID" != "X" ]; then
return $PID
fi
return 0
}
case $ACTION in
start)
ftpproxy_start
exit $?
;;
stop)
ftpproxy_stop
exit $?
;;
restart)
ftpproxy_restart
exit $?
;;
status)
ftpproxy_status
if [ $? -gt 0 ]; then
exit 0
fi
exit 1
;;
esac

View file

@ -0,0 +1,23 @@
[start]
command:/usr/local/opnsense/scripts/OPNsense/FtpProxy/FtpProxy.sh start
parameters:%s
type:script
message:starting ftpproxy
[stop]
command:/usr/local/opnsense/scripts/OPNsense/FtpProxy/FtpProxy.sh stop
parameters:%s
type:script
message:stopping ftpproxy
[status]
command:/usr/local/opnsense/scripts/OPNsense/FtpProxy/FtpProxy.sh status
parameters:%s
type:script
message:get ftpproxy status
[restart]
command:/usr/local/opnsense/scripts/OPNsense/FtpProxy/FtpProxy.sh restart
parameters:%s
type:script
message:restarting ftpproxy

View file

@ -0,0 +1,6 @@
name: ftpproxy
version: 0.1
origin: opnsense/ftpproxy
comment: ftp-proxy configuration
desc: configuration templates for ftp-proxy
prefix: /

View file

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

View file

@ -0,0 +1,39 @@
# DO NOT EDIT THIS FILE -- OPNsense auto-generated file
{% if helpers.exists('OPNsense.ftpproxy.ftpproxies.ftpproxy') %}
ftpproxy_enable="YES"
{% set Instances=[] %}
{% for ftpproxy in helpers.toList('OPNsense.ftpproxy.ftpproxies.ftpproxy') %}
{% if ftpproxy.enabled|default('0') == '1' %}
{% set Parameters=[] %}
{% do Parameters.append("-b " ~ ftpproxy.listenaddress) %}
{% do Parameters.append("-p " ~ ftpproxy.listenport) %}
{% if ftpproxy.sourceaddress %}
{% do Parameters.append("-a " ~ ftpproxy.sourceaddress) %}
{% endif %}
{% if ftpproxy.rewritesourceport|default('0') == '1' %}
{% do Parameters.append("-r") %}
{% endif %}
{% if ftpproxy.idletimeout|default('86400') != '86400' %}
{% do Parameters.append("-t " ~ ftpproxy.idletimeout) %}
{% endif %}
{% if ftpproxy.maxsessions|default('100') != '100' %}
{% do Parameters.append("-m " ~ ftpproxy.maxsessions) %}
{% endif %}
{% if ftpproxy.reverseaddress %}
{% do Parameters.append("-R " ~ ftpproxy.reverseaddress) %}
{% if ftpproxy.reverseport|default('21') != '21' %}
{% do Parameters.append("-P " ~ ftpproxy.reverseport) %}
{% endif %}
{% endif %}
{% if ftpproxy.logconnections|default('0') == '1' %}
{% do Parameters.append("-v") %}
{% endif %}
{% if ftpproxy.debuglevel|default('5') != '5' %}
{% do Parameters.append("-D " ~ ftpproxy.debuglevel) %}
{% endif %}
ftpproxy_id{{loop.index}}="{% for Parameter in Parameters %} {{Parameter}}{% endfor %}"
{% do Instances.append(loop.index) %}
{% endif %}
{% endfor %}
ftpproxy_instances="{% for Instance in Instances %} {{"id" ~ Instance}}{% endfor %}"
{% endif %}