diff --git a/README.md b/README.md index 0750932db..a448c0181 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ security/acme-client -- Let's Encrypt client security/clamav -- Antivirus engine for detecting malicious threats security/intrusion-detection-content-pt-open -- IDS PT Research ruleset (only for non-commercial use) security/tinc -- Tinc VPN +security/tor -- The Onion Router www/c-icap -- c-icap connects your Proxy with a virus scanner www/web-proxy-sso -- Add SSO Active Directory to use in Proxy ``` diff --git a/security/tor/Makefile b/security/tor/Makefile new file mode 100644 index 000000000..b6d00b634 --- /dev/null +++ b/security/tor/Makefile @@ -0,0 +1,8 @@ +PLUGIN_NAME= tor +PLUGIN_VERSION= 0.1 +PLUGIN_COMMENT= The Onion Router +PLUGIN_DEPENDS= tor +PLUGIN_MAINTAINER= franz.fabian.94@gmail.com +PLUGIN_DEVEL= yes + +.include "../../Mk/plugins.mk" diff --git a/security/tor/src/etc/inc/plugins.inc.d/tor.inc b/security/tor/src/etc/inc/plugins.inc.d/tor.inc new file mode 100644 index 000000000..b30b05ec2 --- /dev/null +++ b/security/tor/src/etc/inc/plugins.inc.d/tor.inc @@ -0,0 +1,65 @@ +enabled == '1') { + return true; + } + + return false; +} + +function tor_firewall($fw) +{ + if (tor_enabled()) { + } +} + +function tor_services() +{ + global $config; + + $services = array(); + + if (tor_enabled()) { + $services[] = array( + 'description' => gettext('The Onion Router'), + 'configd' => array( + 'restart' => array('tor restart'), + 'start' => array('tor start'), + 'stop' => array('tor stop'), + ), + 'name' => 'tor', + 'pidfile' => '/var/run/tor/tor.pid' + ); + } + + return $services; +} diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/ExitaclController.php b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/ExitaclController.php new file mode 100644 index 000000000..708603361 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/ExitaclController.php @@ -0,0 +1,169 @@ +sessionClose(); + $mdl = $this->getModel(); + $grid = new UIModelGrid($mdl->policy); + return $grid->fetchBindRequest( + $this->request, + array('enabled', 'type', 'network', 'action', 'startport', 'endport') + ); + } + public function getaclAction($uuid = null) + { + $mdl = $this->getModel(); + if ($uuid != null) { + $node = $mdl->getNodeByReference('policy.' . $uuid); + if ($node != null) { + // return node + return array('exitpolicy' => $node->getNodes()); + } + } else { + $node = $mdl->policy->add(); + return array('exitpolicy' => $node->getNodes()); + } + return array(); + } + public function addaclAction() + { + $result = array('result' => 'failed'); + if ($this->request->isPost() && $this->request->hasPost('exitpolicy')) { + $result = array('result' => 'failed', 'validations' => array()); + $mdl = $this->getModel(); + $node = $mdl->policy->Add(); + $node->setNodes($this->request->getPost('exitpolicy')); + $valMsgs = $mdl->performValidation(); + + foreach ($valMsgs as $field => $msg) { + $fieldnm = str_replace($node->__reference, 'exitpolicy', $msg->getField()); + $result['validations'][$fieldnm] = $msg->getMessage(); + } + + if (count($result['validations']) == 0) { + $mdl->serializeToConfig(); + Config::getInstance()->save(); + unset($result['validations']); + $result['result'] = 'saved'; + } + } + return $result; + } + public function delaclAction($uuid) + { + + $result = array('result' => 'failed'); + + if ($this->request->isPost()) { + $mdl = $this->getModel(); + if ($uuid != null) { + if ($mdl->policy->del($uuid)) { + $mdl->serializeToConfig(); + Config::getInstance()->save(); + $result['result'] = 'deleted'; + } else { + $result['result'] = 'not found'; + } + } + } + return $result; + } + public function setaclAction($uuid) + { + if ($this->request->isPost() && $this->request->hasPost('exitpolicy')) { + $mdl = $this->getModel(); + if ($uuid != null) { + $node = $mdl->getNodeByReference('policy.' . $uuid); + if ($node != null) { + $result = array('result' => 'failed', 'validations' => array()); + $info = $this->request->getPost('exitpolicy'); + + $node->setNodes($info); + $valMsgs = $mdl->performValidation(); + foreach ($valMsgs as $field => $msg) { + $fieldnm = str_replace($node->__reference, 'exitpolicy', $msg->getField()); + $result['validations'][$fieldnm] = $msg->getMessage(); + } + + if (count($result['validations']) == 0) { + // save config if validated correctly + $mdl->serializeToConfig(); + unset($result['validations']); + Config::getInstance()->save(); + $result = array('result' => 'saved'); + } + return $result; + } + } + } + return array('result' => 'failed'); + } + public function toggle_handler($uuid, $element) + { + + $result = array('result' => 'failed'); + + if ($this->request->isPost()) { + $mdl = $this->getModel(); + if ($uuid != null) { + $node = $mdl->getNodeByReference($element . '.' . $uuid); + if ($node != null) { + if ($node->enabled->__toString() == '1') { + $result['result'] = 'Disabled'; + $node->enabled = '0'; + } else { + $result['result'] = 'Enabled'; + $node->enabled = '1'; + } + $mdl->serializeToConfig(); + Config::getInstance()->save(); + } + } + } + return $result; + } + + public function toggleaclAction($uuid) + { + return $this->toggle_handler($uuid, 'policy'); + } +} diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/GeneralController.php b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/GeneralController.php new file mode 100644 index 000000000..b24cb65c4 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/GeneralController.php @@ -0,0 +1,73 @@ +'failed'); + if ($this->request->isPost()) { + $mdl = new General(); + $mdl->setNodes($this->request->getPost('general')); + + // perform validation + $valMsgs = $mdl->performValidation(); + foreach ($valMsgs as $field => $msg) { + if (!array_key_exists('validations', $result)) { + $result['validations'] = array(); + } + $result['validations']['general.'.$msg->getField()] = $msg->getMessage(); + } + + if ($valMsgs->count() == 0) { + if (empty((string)$mdl->control_port_password) || empty((string)$mdl->control_port_password_hashed)) { + $backend = new Backend(); + $keys = json_decode(trim($backend->configdRun('tor genkey')), true); + $mdl->control_port_password_hashed = $keys['hashed_control_password']; + $mdl->control_port_password = $keys['control_password']; + } + $mdl->serializeToConfig(); + Config::getInstance()->save(); + $result['result'] = 'saved'; + } + } + return $result; + } +} diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/HiddenserviceController.php b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/HiddenserviceController.php new file mode 100644 index 000000000..5d6ea6617 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/HiddenserviceController.php @@ -0,0 +1,171 @@ +sessionClose(); + $mdl = $this->getModel(); + $grid = new UIModelGrid($mdl->service); + return $grid->fetchBindRequest( + $this->request, + array('enabled', 'name') + ); + } + public function getserviceAction($uuid = null) + { + $mdl = $this->getModel(); + if ($uuid != null) { + $node = $mdl->getNodeByReference('service.' . $uuid); + if ($node != null) { + // return node + return array('hiddenservice' => $node->getNodes()); + } + } else { + $node = $mdl->service->add(); + return array('hiddenservice' => $node->getNodes()); + } + return array(); + } + public function addserviceAction() + { + $result = array('result' => 'failed'); + if ($this->request->isPost() && $this->request->hasPost('hiddenservice')) { + $result = array('result' => 'failed', 'validations' => array()); + $mdl = $this->getModel(); + $node = $mdl->service->Add(); + $node->setNodes($this->request->getPost('hiddenservice')); + $valMsgs = $mdl->performValidation(); + + foreach ($valMsgs as $field => $msg) { + $fieldnm = str_replace($node->__reference, 'hiddenservice', $msg->getField()); + $result['validations'][$fieldnm] = $msg->getMessage(); + } + + if (count($result['validations']) == 0) { + // save config if validated correctly + $mdl->serializeToConfig(); + Config::getInstance()->save(); + unset($result['validations']); + $result['result'] = 'saved'; + } + } + return $result; + } + public function delserviceAction($uuid) + { + + $result = array('result' => 'failed'); + + if ($this->request->isPost()) { + $mdl = $this->getModel(); + if ($uuid != null) { + if ($mdl->service->del($uuid)) { + $mdl->serializeToConfig(); + Config::getInstance()->save(); + $result['result'] = 'deleted'; + } else { + $result['result'] = 'not found'; + } + } + } + return $result; + } + public function setserviceAction($uuid) + { + if ($this->request->isPost() && $this->request->hasPost('hiddenservice')) { + $mdl = $this->getModel(); + if ($uuid != null) { + $node = $mdl->getNodeByReference('service.' . $uuid); + if ($node != null) { + $result = array('result' => 'failed', 'validations' => array()); + $info = $this->request->getPost('hiddenservice'); + + $node->setNodes($info); + $valMsgs = $mdl->performValidation(); + foreach ($valMsgs as $field => $msg) { + $fieldnm = str_replace($node->__reference, 'hiddenservice', $msg->getField()); + $result['validations'][$fieldnm] = $msg->getMessage(); + } + + if (count($result['validations']) == 0) { + // save config if validated correctly + $mdl->serializeToConfig(); + unset($result['validations']); + Config::getInstance()->save(); + $result = array('result' => 'saved'); + } + return $result; + } + } + } + return array('result' => 'failed'); + } + public function toggle_handler($uuid, $element) + { + + $result = array('result' => 'failed'); + + if ($this->request->isPost()) { + $mdl = $this->getModel(); + if ($uuid != null) { + $node = $mdl->getNodeByReference($element . '.' . $uuid); + if ($node != null) { + if ($node->enabled->__toString() == '1') { + $result['result'] = 'Disabled'; + $node->enabled = '0'; + } else { + $result['result'] = 'Enabled'; + $node->enabled = '1'; + } + // if item has toggled, serialize to config and save + $mdl->serializeToConfig(); + Config::getInstance()->save(); + } + } + } + return $result; + } + + public function toggleserviceAction($uuid) + { + return $this->toggle_handler($uuid, 'service'); + } +} diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/HiddenserviceaclController.php b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/HiddenserviceaclController.php new file mode 100644 index 000000000..8688b7fec --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/HiddenserviceaclController.php @@ -0,0 +1,170 @@ +sessionClose(); + $mdl = $this->getModel(); + $grid = new UIModelGrid($mdl->hiddenserviceacl); + return $grid->fetchBindRequest( + $this->request, + array('enabled', 'hiddenservice', 'port', 'target_host', 'target_port') + ); + } + public function getaclAction($uuid = null) + { + $mdl = $this->getModel(); + if ($uuid != null) { + $node = $mdl->getNodeByReference('hiddenserviceacl.' . $uuid); + if ($node != null) { + // return node + return array('hiddenserviceacl' => $node->getNodes()); + } + } else { + $node = $mdl->hiddenserviceacl->add(); + return array('hiddenserviceacl' => $node->getNodes()); + } + return array(); + } + public function addaclAction() + { + $result = array('result' => 'failed'); + if ($this->request->isPost() && $this->request->hasPost('hiddenserviceacl')) { + $result = array('result' => 'failed', 'validations' => array()); + $mdl = $this->getModel(); + $node = $mdl->hiddenserviceacl->Add(); + $node->setNodes($this->request->getPost('hiddenserviceacl')); + $valMsgs = $mdl->performValidation(); + + foreach ($valMsgs as $field => $msg) { + $fieldnm = str_replace($node->__reference, 'hiddenserviceacl', $msg->getField()); + $result['validations'][$fieldnm] = $msg->getMessage(); + } + + if (count($result['validations']) == 0) { + // save config if validated correctly + $mdl->serializeToConfig(); + Config::getInstance()->save(); + unset($result['validations']); + $result['result'] = 'saved'; + } + } + return $result; + } + public function delaclAction($uuid) + { + + $result = array('result' => 'failed'); + + if ($this->request->isPost()) { + $mdl = $this->getModel(); + if ($uuid != null) { + if ($mdl->hiddenserviceacl->del($uuid)) { + $mdl->serializeToConfig(); + Config::getInstance()->save(); + $result['result'] = 'deleted'; + } else { + $result['result'] = 'not found'; + } + } + } + return $result; + } + public function setaclAction($uuid) + { + if ($this->request->isPost() && $this->request->hasPost('hiddenserviceacl')) { + $mdl = $this->getModel(); + if ($uuid != null) { + $node = $mdl->getNodeByReference('hiddenserviceacl.' . $uuid); + if ($node != null) { + $result = array('result' => 'failed', 'validations' => array()); + $info = $this->request->getPost('hiddenserviceacl'); + + $node->setNodes($info); + $valMsgs = $mdl->performValidation(); + foreach ($valMsgs as $field => $msg) { + $fieldnm = str_replace($node->__reference, 'hiddenserviceacl', $msg->getField()); + $result['validations'][$fieldnm] = $msg->getMessage(); + } + + if (count($result['validations']) == 0) { + // save config if validated correctly + $mdl->serializeToConfig(); + unset($result['validations']); + Config::getInstance()->save(); + $result = array('result' => 'saved'); + } + return $result; + } + } + } + return array('result' => 'failed'); + } + public function toggle_handler($uuid, $element) + { + + $result = array('result' => 'failed'); + + if ($this->request->isPost()) { + $mdl = $this->getModel(); + if ($uuid != null) { + $node = $mdl->getNodeByReference($element . '.' . $uuid); + if ($node != null) { + if ($node->enabled->__toString() == '1') { + $result['result'] = 'Disabled'; + $node->enabled = '0'; + } else { + $result['result'] = 'Enabled'; + $node->enabled = '1'; + } + $mdl->serializeToConfig(); + Config::getInstance()->save(); + } + } + } + return $result; + } + + public function toggleaclAction($uuid) + { + return $this->toggle_handler($uuid, 'hiddenserviceacl'); + } +} diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/RelayController.php b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/RelayController.php new file mode 100644 index 000000000..088602ec0 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/RelayController.php @@ -0,0 +1,38 @@ +request->isPost()) { + $backend = new Backend(); + $response = $backend->configdRun('tor start'); + $backend->configdRun('filter reload'); + return array('response' => $response); + } else { + return array('response' => array()); + } + } + + /** + * stop tor service + * @return array + */ + public function stopAction() + { + if ($this->request->isPost()) { + $backend = new Backend(); + $response = $backend->configdRun('tor stop'); + return array('response' => $response); + } else { + return array('response' => array()); + } + } + + /** + * query tor hidden service hostnames + * @return array + */ + public function get_hidden_servicesAction() + { + $backend = new Backend(); + $response = json_decode($backend->configdRun('tor gethostnames')); + return array('response' => $response); + } + + /** + * restart tor service + * @return array + */ + public function restartAction() + { + if ($this->request->isPost()) { + $backend = new Backend(); + $response = $backend->configdRun('tor restart'); + $backend->configdRun('filter reload'); + return array('response' => $response); + } else { + return array('response' => array()); + } + } + + /** + * retrieve status of tor + * @return array + * @throws \Exception + */ + public function statusAction() + { + $backend = new Backend(); + $general = new General(); + $response = $backend->configdRun('tor status'); + + if (strpos($response, 'not running') > 0) { + if ($general->enabled->__toString() == 1) { + $status = 'stopped'; + } else { + $status = 'disabled'; + } + } elseif (strpos($response, 'is running') > 0) { + $status = 'running'; + } elseif ($general->enabled->__toString() == 0) { + $status = 'disabled'; + } else { + $status = 'unknown'; + } + + + return array('status' => $status); + } + + /** + * reconfigure tor, generate config and reload + */ + public function reconfigureAction() + { + if ($this->request->isPost()) { + // close session for long running action + $this->sessionClose(); + + $general = new General(); + $backend = new Backend(); + + $runStatus = $this->statusAction(); + + // stop tor if it is running or not + $this->stopAction(); + + // generate template + $backend->configdRun('template reload OPNsense/Tor'); + + // (re)start daemon + if ($general->enabled->__toString() == 1) { + $this->startAction(); + } + + return array('status' => 'ok'); + } else { + return array('status' => 'failed'); + } + } +} diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/SocksaclController.php b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/SocksaclController.php new file mode 100644 index 000000000..0a357852c --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/Api/SocksaclController.php @@ -0,0 +1,169 @@ +sessionClose(); + $mdl = $this->getModel(); + $grid = new UIModelGrid($mdl->policy); + return $grid->fetchBindRequest( + $this->request, + array('enabled', 'type', 'network', 'action') + ); + } + public function getaclAction($uuid = null) + { + $mdl = $this->getModel(); + if ($uuid != null) { + $node = $mdl->getNodeByReference('policy.' . $uuid); + if ($node != null) { + // return node + return array('policy' => $node->getNodes()); + } + } else { + $node = $mdl->policy->add(); + return array('policy' => $node->getNodes()); + } + return array(); + } + public function addaclAction() + { + $result = array('result' => 'failed'); + if ($this->request->isPost() && $this->request->hasPost('policy')) { + $result = array('result' => 'failed', 'validations' => array()); + $mdl = $this->getModel(); + $node = $mdl->policy->Add(); + $node->setNodes($this->request->getPost('policy')); + $valMsgs = $mdl->performValidation(); + + foreach ($valMsgs as $field => $msg) { + $fieldnm = str_replace($node->__reference, 'policy', $msg->getField()); + $result['validations'][$fieldnm] = $msg->getMessage(); + } + + if (count($result['validations']) == 0) { + $mdl->serializeToConfig(); + Config::getInstance()->save(); + unset($result['validations']); + $result['result'] = 'saved'; + } + } + return $result; + } + public function delaclAction($uuid) + { + + $result = array('result' => 'failed'); + + if ($this->request->isPost()) { + $mdl = $this->getModel(); + if ($uuid != null) { + if ($mdl->policy->del($uuid)) { + $mdl->serializeToConfig(); + Config::getInstance()->save(); + $result['result'] = 'deleted'; + } else { + $result['result'] = 'not found'; + } + } + } + return $result; + } + public function setaclAction($uuid) + { + if ($this->request->isPost() && $this->request->hasPost('policy')) { + $mdl = $this->getModel(); + if ($uuid != null) { + $node = $mdl->getNodeByReference('policy.' . $uuid); + if ($node != null) { + $result = array('result' => 'failed', 'validations' => array()); + $info = $this->request->getPost('policy'); + + $node->setNodes($info); + $valMsgs = $mdl->performValidation(); + foreach ($valMsgs as $field => $msg) { + $fieldnm = str_replace($node->__reference, 'policy', $msg->getField()); + $result['validations'][$fieldnm] = $msg->getMessage(); + } + + if (count($result['validations']) == 0) { + // save config if validated correctly + $mdl->serializeToConfig(); + unset($result['validations']); + Config::getInstance()->save(); + $result = array('result' => 'saved'); + } + return $result; + } + } + } + return array('result' => 'failed'); + } + public function toggle_handler($uuid, $element) + { + + $result = array('result' => 'failed'); + + if ($this->request->isPost()) { + $mdl = $this->getModel(); + if ($uuid != null) { + $node = $mdl->getNodeByReference($element . '.' . $uuid); + if ($node != null) { + if ($node->enabled->__toString() == '1') { + $result['result'] = 'Disabled'; + $node->enabled = '0'; + } else { + $result['result'] = 'Enabled'; + $node->enabled = '1'; + } + $mdl->serializeToConfig(); + Config::getInstance()->save(); + } + } + } + return $result; + } + + public function toggleaclAction($uuid) + { + return $this->toggle_handler($uuid, 'policy'); + } +} diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/IndexController.php b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/IndexController.php new file mode 100644 index 000000000..b2e0739a0 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/IndexController.php @@ -0,0 +1,51 @@ +view->title = gettext("The Onion Router"); + $this->view->general = $this->getForm("general"); + $this->view->toracl = $this->getForm("acl_sockspolicy"); + $this->view->hidden_service = $this->getForm("hidden_service"); + $this->view->hidden_service_acl = $this->getForm("hidden_service_acl"); + $this->view->relay = $this->getForm("relay"); + $this->view->exitpolicy = $this->getForm("acl_exitpolicy"); + $this->view->pick('OPNsense/Tor/general'); + } +} diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/acl_exitpolicy.xml b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/acl_exitpolicy.xml new file mode 100644 index 000000000..5e33e3331 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/acl_exitpolicy.xml @@ -0,0 +1,37 @@ +
+ + exitpolicy.enabled + + checkbox + Enabling this will write the policy to the config file. + + + exitpolicy.type + + dropdown + + + exitpolicy.network + + text + Network on which this ACL is appied. + + + exitpolicy.startport + + text + Begin of the port range or the port used by this ACL. + + + exitpolicy.endport + + text + Enter an end port if you want to use a port range. + + + exitpolicy.action + + dropdown + Reject: Do not allow the connection; Accept: Pass the connection + +
diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/acl_sockspolicy.xml b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/acl_sockspolicy.xml new file mode 100644 index 000000000..85e29c1fd --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/acl_sockspolicy.xml @@ -0,0 +1,25 @@ +
+ + policy.enabled + + checkbox + Enabling this will write the policy to the config file. + + + policy.type + + dropdown + + + policy.network + + text + Network on which this ACL is appied. + + + policy.action + + dropdown + Reject: Do not allow the connection; Accept: Pass the connection + +
diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/general.xml b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/general.xml new file mode 100644 index 000000000..4c2feea4b --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/general.xml @@ -0,0 +1,104 @@ +
+ + general.enabled + + checkbox + This will activate the onion router. + + + general.socks_listen_ip + + select_multiple + + Add more than localhost IP addresses to listen IPs for SOCKS connections. + + + general.socks_listen_port + + text + Port number on which the SOCKS server should listen. The default is 9050. You should not change this unless you need the port. + true + + + general.control_port + + text + Control Port number on which tor should listen. The default is 9051. You should not change this unless you need the port. + true + + + general.enablelogfile + + checkbox + If you check this, a log file will be written to disk. + + + general.logfilelevel + + dropdown + + This is the detail level of the log. A higher level means more data is logged. + + + general.enablesyslog + + checkbox + Syslog is a service which is made to collect log messages from different software and maybe to a central logging server. Check this box if you have such a setup. + + + general.sysloglevel + + dropdown + + This is the detail level of the log. A higher level means more data is logged. + + + general.fascist_firewall + + checkbox + This try to circumvent censorship. Please note that the fascist mode does not support "Hidden Services". + + + general.fascist_firewall_ports + + select_multiple + Open ports by the fascist firewall. + true + + + + general.enable_transparent + + checkbox + Enable this, if you want to anonymize traffic originating from a network. + true + + + general.transparent_port + + text + Port number on which the transparent proxy server should listen. The default is 9040. You should not change this unless you need the port. + true + + + general.transparent_dns + + text + Port number on which the transparent DNS server should listen. The default is 9053. You should not change this unless you need the port. + true + + + general.transparent_ip_pool + + text + Enter a network which will be used as an IP Pool to map onion Hosts. + true + + + general.dns_map_hosts + + checkbox + Enable this, if you want to map onion services to the IP pool configured above. + true + +
diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/hidden_service.xml b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/hidden_service.xml new file mode 100644 index 000000000..b1827ad43 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/hidden_service.xml @@ -0,0 +1,14 @@ +
+ + hiddenservice.enabled + + checkbox + Enable this hidden service. + + + hiddenservice.name + + text + Enter a directory name for the hidden service. It may consist of lowercase and uppercase characters. + +
diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/hidden_service_acl.xml b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/hidden_service_acl.xml new file mode 100644 index 000000000..70c7979c3 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/hidden_service_acl.xml @@ -0,0 +1,31 @@ +
+ + hiddenserviceacl.enabled + + checkbox + Enable this hidden service. + + + hiddenserviceacl.hiddenservice + + dropdown + + + hiddenserviceacl.port + + text + The port number which is exposed in the Tor network. + + + hiddenserviceacl.target_host + + text + The Target host. Be careful when using localhost as some services may bypass ACLs when connecting from "127.0.0.1" or "::1". + + + hiddenserviceacl.target_port + + text + Enter the port of your target server. + +
diff --git a/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/relay.xml b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/relay.xml new file mode 100644 index 000000000..02224c367 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/controllers/OPNsense/Tor/forms/relay.xml @@ -0,0 +1,62 @@ +
+ + relay.enabled + + checkbox + Act as a relay. + + + relay.host + + text + true + + + relay.port + + text + + + relay.address + + text + The external FQDN of this host. + + + relay.nick + + text + This may only consist of characters and numbers and is used to identifiy your host. + + + relay.bandwithrate + + text + + + relay.bandwithburst + + text + + + relay.directory_port + + text + + + relay.exitrejectprivateip + + checkbox + + + relay.relay + + checkbox + A bridge is a private relay. It is not visible in the public directory. Check this if you want to become a relay (public). + + + relay.publish + + checkbox + +
diff --git a/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/ACL/ACL.xml b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/ACL/ACL.xml new file mode 100644 index 000000000..e54af9693 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/ACL/ACL.xml @@ -0,0 +1,9 @@ + + + tor + + ui/tor/* + api/tor/* + + + diff --git a/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/ACLExitPolicy.php b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/ACLExitPolicy.php new file mode 100644 index 000000000..68369c7a5 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/ACLExitPolicy.php @@ -0,0 +1,34 @@ + + //OPNsense/tor/exitpolicy + ACL for Socks port + + + + 1 + Y + + + v6 + Y + + IPv4 + IPv6 + + + + Y + + + 1 + N + 65535 + A valid Port number must be specified. + + + 1 + N + 65535 + A valid Port number must be specified. + + + accept + Y + + Accept + Reject + + + + + diff --git a/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/ACLSocksPolicy.php b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/ACLSocksPolicy.php new file mode 100644 index 000000000..e540e82e0 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/ACLSocksPolicy.php @@ -0,0 +1,34 @@ + + //OPNsense/tor/aclsockspolicy + ACL for Socks port + + + + 1 + Y + + + v6 + Y + + IPv4 + IPv6 + + + + Y + + + accept + Y + + Accept + Reject + + + + + diff --git a/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/General.php b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/General.php new file mode 100644 index 000000000..f82eeec35 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/General.php @@ -0,0 +1,34 @@ + + //OPNsense/tor/general + General Tor configuration + + + 0 + Y + + + N + Y + + + 9050 + 0 + Y + 65535 + A valid Port number must be specified. + + + 9051 + 0 + N + 65535 + A valid Port number must be specified. + + + N + /^.+$/ + + + N + /^.+$/ + + + 0 + Y + + + Y + N + notifications + + Errors + Warnings + Notifications + Informational + Debugging + + + + 0 + Y + + + Y + N + notifications + + Errors + Warnings + Notifications + Informational + Debugging + + + + 0 + Y + + + 80,443 + Y + /^(\d+,)*\d+$/ + + + 0 + Y + + + 9040 + 0 + Y + 65535 + A valid Port number must be specified. + + + 9053 + 0 + Y + 65535 + A valid Port number must be specified. + + + 172.29.0.0/16 + Y + + + 0 + Y + + + diff --git a/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/HiddenService.php b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/HiddenService.php new file mode 100644 index 000000000..988a55f26 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/HiddenService.php @@ -0,0 +1,34 @@ + + //OPNsense/tor/hiddenservice + Tor hidden service configuration + + + + 1 + Y + + + Y + /^[a-z0-9_-]+$/i + The name should only consist of alphanumeric characters, dashes and underscores. + + + + diff --git a/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/HiddenServiceACL.php b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/HiddenServiceACL.php new file mode 100644 index 000000000..8b6ee3441 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/HiddenServiceACL.php @@ -0,0 +1,34 @@ + + //OPNsense/tor/hiddenserviceacl + Tor Hidden Service ACL + + + + 1 + Y + + + + + + A hidden service must be set. + N + Y + + + 80 + 1 + Y + 65535 + A valid Port number must be specified. + + + Y + 127.0.0.1 + + + 80 + 1 + Y + 65535 + A valid Port number must be specified. + + + + diff --git a/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/Menu/Menu.xml b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/Menu/Menu.xml new file mode 100644 index 000000000..9dd1e7b47 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/Menu/Menu.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/Relay.php b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/Relay.php new file mode 100644 index 000000000..3a2caa6c0 --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/models/OPNsense/Tor/Relay.php @@ -0,0 +1,34 @@ + + //OPNsense/tor/relay + Tor Relay configuration + + + 0 + Y + + + N + /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|[a-f0-9:]{2,})$/i + + + 9001 + 0 + Y + 65535 + A valid Port number must be specified. + + + 1 + N + 65535 + A valid Port number must be specified. + +
+ N + + /^[a-z0-9.-]+$/i +
+ + /^.+$/ + N + + /^[a-zA-Z0-9]+$/ + + + N + + + N + + + 0 + Y + + + 1 + Y + + + 0 + Y + +
+ diff --git a/security/tor/src/opnsense/mvc/app/views/OPNsense/Tor/general.volt b/security/tor/src/opnsense/mvc/app/views/OPNsense/Tor/general.volt new file mode 100644 index 000000000..ab1db2e0a --- /dev/null +++ b/security/tor/src/opnsense/mvc/app/views/OPNsense/Tor/general.volt @@ -0,0 +1,240 @@ +{# + + Copyright (C) 2017 Fabian Franz + 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. + + +#} + + + + + +
+
+ {{ partial("layout_partials/base_form",['fields': general,'id':'general'])}} +
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + +
{{ lang._('Enabled') }}{{ lang._('Action') }}{{ lang._('Protocol') }}{{ lang._('Network') }}{{ lang._('ID') }}{{ lang._('Commands') }}
+ + +
+
+
+ + + + + + + + + + + + + + + + + +
{{ lang._('Enabled') }}{{ lang._('Name') }}{{ lang._('ID') }}{{ lang._('Commands') }}
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + +
{{ lang._('Enabled') }}{{ lang._('Hidden Service') }}{{ lang._('Port') }}{{ lang._('Target Host') }}{{ lang._('Target Port') }}{{ lang._('ID') }}{{ lang._('Commands') }}
+ + +
+
+
+ {{ partial("layout_partials/base_form",['fields': relay,'id':'relay'])}} +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + +
{{ lang._('Enabled') }}{{ lang._('Action') }}{{ lang._('Protocol') }}{{ lang._('Network') }}{{ lang._('Start Port') }}{{ lang._('End Port') }}{{ lang._('ID') }}{{ lang._('Commands') }}
+ + +
+
+
+ +{{ partial("layout_partials/base_dialog",['fields': toracl,'id':'toracldlg', 'label':lang._('Edit ACL Entry')]) }} +{{ partial("layout_partials/base_dialog",['fields': hidden_service,'id':'hiddenservicedlg', 'label':lang._('Edit Hidden Service')]) }} +{{ partial("layout_partials/base_dialog",['fields': hidden_service_acl,'id':'hiddenserviceacl', 'label':lang._('Edit Hidden Service Route')]) }} +{{ partial("layout_partials/base_dialog",['fields': exitpolicy,'id':'torexitacldlg', 'label':lang._('Edit Exit Node ACL')]) }} diff --git a/security/tor/src/opnsense/service/conf/actions.d/actions_tor.conf b/security/tor/src/opnsense/service/conf/actions.d/actions_tor.conf new file mode 100644 index 000000000..bb9387df6 --- /dev/null +++ b/security/tor/src/opnsense/service/conf/actions.d/actions_tor.conf @@ -0,0 +1,35 @@ +[start] +command:/usr/local/opnsense/scripts/tor/setup.sh;/usr/local/etc/rc.d/tor start +parameters: +type:script +message:starting tor + +[stop] +command:/usr/local/etc/rc.d/tor onestop +parameters: +type:script +message:stopping tor + +[restart] +command:/usr/local/opnsense/scripts/tor/setup.sh;/usr/local/etc/rc.d/tor restart +parameters: +type:script +message:restarting tor + +[status] +command:/usr/local/etc/rc.d/tor status;exit 0 +parameters: +type:script_output +message:request tor status + +[genkey] +command:/usr/local/opnsense/service/scripts/tor/gen_key +parameters: +type:script_output +message:generate Tor control key + +[gethostnames] +command:/usr/local/opnsense/service/scripts/tor/get_hostnames +parameters: +type:script_output +message:query hostnames of hidden services diff --git a/security/tor/src/opnsense/service/scripts/tor/gen_key b/security/tor/src/opnsense/service/scripts/tor/gen_key new file mode 100755 index 000000000..9833a0b64 --- /dev/null +++ b/security/tor/src/opnsense/service/scripts/tor/gen_key @@ -0,0 +1,9 @@ +#!/bin/sh + +PASSWORD=$( /usr/local/bin/openssl rand -base64 32 ) +echo "{" +echo -n " \"hashed_control_password\":\"" +tor --quiet --hash-password "$PASSWORD" | tr -d '\n' | tr -d '\r' +echo "\"," +echo " \"control_password\":\"$PASSWORD\"" +echo "}" diff --git a/security/tor/src/opnsense/service/scripts/tor/get_hostnames b/security/tor/src/opnsense/service/scripts/tor/get_hostnames new file mode 100755 index 000000000..a108dc51a --- /dev/null +++ b/security/tor/src/opnsense/service/scripts/tor/get_hostnames @@ -0,0 +1,24 @@ +#!/usr/local/bin/php +service->__items as $service) { + $directory_name = ((string)$service->name); + $hostnamefile = TOR_DATA_DIR . '/' . $directory_name . '/hostname'; + if (file_exists($hostnamefile)) { + $hostname = @file_get_contents($hostnamefile); + } + if (empty($hostname)) { + $hostname = 'not available'; + } + $hostname = trim($hostname); + $hostnames[$directory_name] = $hostname; +} + +print json_encode($hostnames); diff --git a/security/tor/src/opnsense/service/scripts/tor/setup.sh b/security/tor/src/opnsense/service/scripts/tor/setup.sh new file mode 100755 index 000000000..c7680cb15 --- /dev/null +++ b/security/tor/src/opnsense/service/scripts/tor/setup.sh @@ -0,0 +1,7 @@ +#!/bin/sh +mkdir -p /var/db/tor +mkdir -p /var/log/tor +mkdir -p /var/run/tor + +# required to access the pf device for nat +/usr/sbin/pw groupmod proxy -m _tor diff --git a/security/tor/src/opnsense/service/scripts/tor/tor_helper.php b/security/tor/src/opnsense/service/scripts/tor/tor_helper.php new file mode 100644 index 000000000..f05179572 --- /dev/null +++ b/security/tor/src/opnsense/service/scripts/tor/tor_helper.php @@ -0,0 +1,3 @@ + + +{% if helpers.exists('OPNsense.tor.relay.directory_port') and OPNsense.tor.relay.directory_port != '' %} +DirPort {{ OPNsense.tor.relay.directory_port }} +{% endif %} +## Uncomment to return an arbitrary blob of html on your DirPort. Now you +## can explain what Tor is if anybody wonders why your IP address is +## contacting them. See contrib/tor-exit-notice.html in Tor's source +## distribution for a sample. +#DirPortFrontPage /usr/local/etc/tor/tor-exit-notice.html + +{% if helpers.exists('OPNsense.tor.relay.exitrejectprivateip') %} +ExitPolicyRejectPrivate {{ OPNsense.tor.relay.exitrejectprivateip }} +{% endif %} + + +{% if helpers.exists('OPNsense.tor.exitpolicy') %} +{% if helpers.exists('OPNsense.tor.exitpolicy.policy') %} +# exit node policy + +{% for policy in helpers.toList('OPNsense.tor.exitpolicy.policy') %} +{% if policy.enabled == '1' %} +ExitPolicy {{ policy.action }}{% if policy.type == 'v6' %}6{% endif + %} {% if policy.network == '' %}*{% if 'v' in policy.type %}{{ policy.type|replace('v','') }}{% endif%}{% else + %}{{ policy.network }}{% endif + %}{% if 'startport' in policy %}:{{ policy.startport + }}{% if 'endport' in policy %}-{{ policy.endport }}{% endif + %}{% endif %} + +{% endif %} +{% endfor %} +{% endif %} +{% endif %} + +# default: don't pass anything +ExitPolicy reject *:* +ExitPolicy reject6 *:* + + +BridgeRelay {{ OPNsense.tor.relay.relay|default('1') }} +PublishServerDescriptor {{ OPNsense.tor.relay.publish|default('0') }} +{% endif %} + +{% endif %}