diff --git a/tor/api/module.php b/tor/api/module.php new file mode 100755 index 0000000..f3a010d --- /dev/null +++ b/tor/api/module.php @@ -0,0 +1,302 @@ +request->action) { + case 'refreshInfo': + $this->refreshInfo(); + break; + case 'refreshStatus': + $this->refreshStatus(); + break; + case 'handleDependencies': + $this->handleDependencies(); + break; + case 'handleDependenciesStatus': + $this->handleDependenciesStatus(); + break; + case 'toggletor': + $this->toggletor(); + break; + case 'refreshHiddenServices': + $this->refreshHiddenServices(); + break; + case 'addHiddenService': + $this->addHiddenService(); + break; + case 'removeHiddenService': + $this->removeHiddenService(); + break; + case 'addServiceForward': + $this->addServiceForward(); + break; + case 'removeServiceForward': + $this->removeServiceForward(); + break; + default: + break; + } + } + + private function success($value) + { + $this->response = array('success' => $value); + } + + private function error($message) + { + $this->response = array('error' => $message); + } + + private function isValidName($name) + { + return preg_match('/^[a-zA-Z0-9_]+$/', $name) === 1; + } + + private function isValidPort($port) + { + return preg_match('/^[0-9]+$/', $port) === 1; + } + + private function isValidRedirectTo($redirect_to) + { + return preg_match('/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+$/', $redirect_to) === 1; + } + + protected function checkDependency($dependencyName) + { + return (exec("which {$dependencyName}") != '' && !file_exists($this->progressFile)); + } + + protected function refreshInfo() + { + $moduleInfo = @json_decode(file_get_contents("/pineapple/modules/tor/module.info")); + $this->response = array('title' => $moduleInfo->title, 'version' => $moduleInfo->version); + } + + protected function checkRunning($processName) + { + return (exec("pgrep '{$processName}$'") != ''); + } + + private function handleDependencies() + { + $destination = ""; + if (isset($this->request->destination)) { + $destination = $this->request->destination; + if ($destination != "internal" && $destination != "sd") { + $this->error(self::INVALID_DESTINATION); + return; + } + } + + if (!$this->checkDependency("tor")) { + $this->execBackground($this->dependenciesScriptFile . " install " . $destination); + } else { + $this->execBackground($this->dependenciesScriptFile . " remove"); + } + $this->success(true); + } + + private function handleDependenciesStatus() + { + if (file_exists($this->progressFile)) { + $this->success(false); + } else { + $this->success(true); + } + } + + private function toggletor() + { + if ($this->checkRunning("tor")) { + exec("/etc/init.d/tor stop"); + } else { + exec("/etc/init.d/tor start"); + } + } + + private function refreshStatus() + { + + $device = $this->getDevice(); + $sdAvailable = $this->isSDAvailable(); + $installed = false; + $install = "Not Installed"; + $processing = false; + + if (file_exists($this->progressFile)) { + // TOR Is installing, please wait. + $install = "Installing..."; + $installLabel = self::WARNING; + $processing = true; + + $status = "Not running"; + $statusLabel = self::DANGER; + } elseif (!$this->checkDependency("tor")) { + // TOR is not installed, please install. + $installLabel = self::DANGER; + + $status = "Start"; + $statusLabel = self::DANGER; + } else { + // TOR is installed, please configure. + $installed = true; + $install = "Installed"; + $installLabel = self::SUCCESS; + + if ($this->checkRunning("tor")) { + $status = "Started"; + $statusLabel = self::SUCCESS; + } else { + $status = "Stopped"; + $statusLabel = self::DANGER; + } + } + + $this->response = array("device" => $device, + "sdAvailable" => $sdAvailable, + "status" => $status, + "statusLabel" => $statusLabel, + "installed" => $installed, + "install" => $install, + "installLabel" => $installLabel, + "processing" => $processing); + } + + private function generateConfig() + { + $output = file_get_contents("/etc/config/tor/torrc"); + $output .= "\n"; + $hiddenServices = @json_decode(file_get_contents($this->moduleConfigFile)); + foreach ($hiddenServices as $hiddenService) { + $output .= "HiddenServiceDir /etc/config/tor/services/{$hiddenService->name}\n"; + $forwards = $hiddenService->forwards; + foreach ($forwards as $forward) { + $output .= "HiddenServicePort {$forward->port} {$forward->redirect_to}\n"; + } + } + file_put_contents("/etc/tor/torrc", $output); + } + + private function reloadTor() + { + $this->generateConfig(); + //Sending SIGHUP to tor process cause config reload. + exec("pkill -sighup tor$"); + } + + private function refreshHiddenServices() + { + $hiddenServices = @json_decode(file_get_contents($this->moduleConfigFile)); + foreach ($hiddenServices as $hiddenService) { + if (file_exists("/etc/config/tor/services/{$hiddenService->name}/hostname")) { + $hostname = file_get_contents("/etc/config/tor/services/{$hiddenService->name}/hostname"); + $hiddenService->hostname = trim($hostname); + } + } + $this->response = array("hiddenServices" => $hiddenServices); + } + + private function addHiddenService() + { + $name = $this->request->name; + if (!$this->isValidName($name)) { + $this->error(self::INVALID_NAME); + return; + } + + $hiddenService = array("name" => $name, "forwards" => array() ); + $hiddenServices = array(); + if (file_exists($this->moduleConfigFile)) { + $hiddenServices = @json_decode(file_get_contents($this->moduleConfigFile)); + } + array_push($hiddenServices, $hiddenService); + file_put_contents($this->moduleConfigFile, @json_encode($hiddenServices, JSON_PRETTY_PRINT)); + $this->reloadTor(); + } + + private function removeHiddenService() + { + $hiddenServices = @json_decode(file_get_contents($this->moduleConfigFile)); + foreach ($hiddenServices as $key => $hiddenService) { + if ($hiddenService->name == $this->request->name) { + unset($hiddenServices[$key]); + } + } + file_put_contents($this->moduleConfigFile, @json_encode($hiddenServices, JSON_PRETTY_PRINT)); + $this->reloadTor(); + } + + private function addServiceForward() + { + $name = $this->request->name; + $port = $this->request->port; + $redirect_to = $this->request->redirect_to; + + if (!$this->isValidName($name)) { + $this->error(self::INVALID_NAME); + return; + } + if (!$this->isValidPort($port)) { + $this->error(self::INVALID_PORT); + return; + } + if (!$this->isValidRedirectTo($redirect_to)) { + $this->error(self::INVALID_DESTINATION); + return; + } + + $hiddenServices = @json_decode(file_get_contents($this->moduleConfigFile)); + foreach ($hiddenServices as $key => $hiddenService) { + if ($hiddenService->name == $name) { + $forwards = $hiddenService->forwards; + $forward = array("port" => $port, "redirect_to" => $redirect_to); + array_push($forwards, $forward); + $hiddenServices[$key]->forwards = $forwards; + } + } + file_put_contents($this->moduleConfigFile, @json_encode($hiddenServices, JSON_PRETTY_PRINT)); + + $this->reloadTor(); + } + + private function removeServiceForward() + { + $name = $this->request->name; + $port = $this->request->port; + $redirect_to = $this->request->redirect_to; + + $hiddenServices = @json_decode(file_get_contents($this->moduleConfigFile)); + foreach ($hiddenServices as $hiddenService) { + if ($hiddenService->name == $name) { + $forwards = $hiddenService->forwards; + foreach ($forwards as $key => $forward) { + if ($forward->port == $port && $forward->redirect_to == $redirect_to) { + unset($forwards[$key]); + } + } + $hiddenService->forwards = $forwards; + } + } + file_put_contents($this->moduleConfigFile, @json_encode($hiddenServices, JSON_PRETTY_PRINT)); + + $this->reloadTor(); + } +} diff --git a/tor/files/torrc b/tor/files/torrc new file mode 100644 index 0000000..b2d4de6 --- /dev/null +++ b/tor/files/torrc @@ -0,0 +1,193 @@ +## Configuration file for a typical Tor user +## Last updated 9 October 2013 for Tor 0.2.5.2-alpha. +## (may or may not work for much older or much newer versions of Tor.) +## +## Lines that begin with "## " try to explain what's going on. Lines +## that begin with just "#" are disabled commands: you can enable them +## by removing the "#" symbol. +## +## See 'man tor', or https://www.torproject.org/docs/tor-manual.html, +## for more options you can use in this file. +## +## Tor will look for this file in various places based on your platform: +## https://www.torproject.org/docs/faq#torrc + +## Tor opens a socks proxy on port 9050 by default -- even if you don't +## configure one below. Set "SocksPort 0" if you plan to run Tor only +## as a relay, and not make any local application connections yourself. +#SocksPort 9050 # Default: Bind to localhost:9050 for local connections. +#SocksPort 192.168.0.1:9100 # Bind to this address:port too. + +## Entry policies to allow/deny SOCKS requests based on IP address. +## First entry that matches wins. If no SocksPolicy is set, we accept +## all (and only) requests that reach a SocksPort. Untrusted users who +## can access your SocksPort may be able to learn about the connections +## you make. +#SocksPolicy accept 192.168.0.0/16 +#SocksPolicy reject * + +## Logs go to stdout at level "notice" unless redirected by something +## else, like one of the below lines. You can have as many Log lines as +## you want. +## +## We advise using "notice" in most cases, since anything more verbose +## may provide sensitive information to an attacker who obtains the logs. +## +## Send all messages of level 'notice' or higher to /var/log/tor/notices.log +#Log notice file /var/log/tor/notices.log +## Send every possible message to /var/log/tor/debug.log +#Log debug file /var/log/tor/debug.log +## Use the system log instead of Tor's logfiles +#Log notice syslog +## To send all messages to stderr: +#Log debug stderr + +## Uncomment this to start the process in the background... or use +## --runasdaemon 1 on the command line. This is ignored on Windows; +## see the FAQ entry if you want Tor to run as an NT service. +RunAsDaemon 1 + +## The directory for keeping all the keys/etc. By default, we store +## things in $HOME/.tor on Unix, and in Application Data\tor on Windows. +DataDirectory /var/lib/tor + +## The port on which Tor will listen for local connections from Tor +## controller applications, as documented in control-spec.txt. +#ControlPort 9051 +## If you enable the controlport, be sure to enable one of these +## authentication methods, to prevent attackers from accessing it. +#HashedControlPassword 16:872860B76453A77D60CA2BB8C1A7042072093276A3D701AD684053EC4C +#CookieAuthentication 1 + +############### This section is just for location-hidden services ### + +## Once you have configured a hidden service, you can look at the +## contents of the file ".../hidden_service/hostname" for the address +## to tell people. +## +## HiddenServicePort x y:z says to redirect requests on port x to the +## address y:z. + +#HiddenServiceDir /var/lib/tor/hidden_service/ +#HiddenServicePort 80 127.0.0.1:80 + +#HiddenServiceDir /var/lib/tor/other_hidden_service/ +#HiddenServicePort 80 127.0.0.1:80 +#HiddenServicePort 22 127.0.0.1:22 + +################ This section is just for relays ##################### +# +## See https://www.torproject.org/docs/tor-doc-relay for details. + +## Required: what port to advertise for incoming Tor connections. +#ORPort 9001 +## If you want to listen on a port other than the one advertised in +## ORPort (e.g. to advertise 443 but bind to 9090), you can do it as +## follows. You'll need to do ipchains or other port forwarding +## yourself to make this work. +#ORPort 443 NoListen +#ORPort 127.0.0.1:9090 NoAdvertise + +## The IP address or full DNS name for incoming connections to your +## relay. Leave commented out and Tor will guess. +#Address noname.example.com + +## If you have multiple network interfaces, you can specify one for +## outgoing traffic to use. +# OutboundBindAddress 10.0.0.5 + +## A handle for your relay, so people don't have to refer to it by key. +#Nickname ididnteditheconfig + +## Define these to limit how much relayed traffic you will allow. Your +## own traffic is still unthrottled. Note that RelayBandwidthRate must +## be at least 20 KB. +## Note that units for these config options are bytes per second, not bits +## per second, and that prefixes are binary prefixes, i.e. 2^10, 2^20, etc. +#RelayBandwidthRate 100 KB # Throttle traffic to 100KB/s (800Kbps) +#RelayBandwidthBurst 200 KB # But allow bursts up to 200KB/s (1600Kbps) + +## Use these to restrict the maximum traffic per day, week, or month. +## Note that this threshold applies separately to sent and received bytes, +## not to their sum: setting "4 GB" may allow up to 8 GB total before +## hibernating. +## +## Set a maximum of 4 gigabytes each way per period. +#AccountingMax 4 GB +## Each period starts daily at midnight (AccountingMax is per day) +#AccountingStart day 00:00 +## Each period starts on the 3rd of the month at 15:00 (AccountingMax +## is per month) +#AccountingStart month 3 15:00 + +## Administrative contact information for this relay or bridge. This line +## can be used to contact you if your relay or bridge is misconfigured or +## something else goes wrong. Note that we archive and publish all +## descriptors containing these lines and that Google indexes them, so +## spammers might also collect them. You may want to obscure the fact that +## it's an email address and/or generate a new address for this purpose. +#ContactInfo Random Person +## You might also include your PGP or GPG fingerprint if you have one: +#ContactInfo 0xFFFFFFFF Random Person + +## Uncomment this to mirror directory information for others. Please do +## if you have enough bandwidth. +#DirPort 9030 # what port to advertise for directory connections +## If you want to listen on a port other than the one advertised in +## DirPort (e.g. to advertise 80 but bind to 9091), you can do it as +## follows. below too. You'll need to do ipchains or other port +## forwarding yourself to make this work. +#DirPort 80 NoListen +#DirPort 127.0.0.1:9091 NoAdvertise +## 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 /etc/tor/tor-exit-notice.html + +## Uncomment this if you run more than one Tor relay, and add the identity +## key fingerprint of each Tor relay you control, even if they're on +## different networks. You declare it here so Tor clients can avoid +## using more than one of your relays in a single circuit. See +## https://www.torproject.org/docs/faq#MultipleRelays +## However, you should never include a bridge's fingerprint here, as it would +## break its concealability and potentionally reveal its IP/TCP address. +#MyFamily $keyid,$keyid,... + +## A comma-separated list of exit policies. They're considered first +## to last, and the first match wins. If you want to _replace_ +## the default exit policy, end this with either a reject *:* or an +## accept *:*. Otherwise, you're _augmenting_ (prepending to) the +## default exit policy. Leave commented to just use the default, which is +## described in the man page or at +## https://www.torproject.org/documentation.html +## +## Look at https://www.torproject.org/faq-abuse.html#TypicalAbuses +## for issues you might encounter if you use the default exit policy. +## +## If certain IPs and ports are blocked externally, e.g. by your firewall, +## you should update your exit policy to reflect this -- otherwise Tor +## users will be told that those destinations are down. +## +## For security, by default Tor rejects connections to private (local) +## networks, including to your public IP address. See the man page entry +## for ExitPolicyRejectPrivate if you want to allow "exit enclaving". +## +#ExitPolicy accept *:6660-6667,reject *:* # allow irc ports but no more +#ExitPolicy accept *:119 # accept nntp as well as default exit policy +#ExitPolicy reject *:* # no exits allowed + +## Bridge relays (or "bridges") are Tor relays that aren't listed in the +## main directory. Since there is no complete public list of them, even an +## ISP that filters connections to all the known Tor relays probably +## won't be able to block all the bridges. Also, websites won't treat you +## differently because they won't know you're running Tor. If you can +## be a real relay, please do; but if not, be a bridge! +#BridgeRelay 1 +## By default, Tor will advertise your bridge to users through various +## mechanisms like https://bridges.torproject.org/. If you want to run +## a private bridge, for example because you'll give out your bridge +## address manually to your friends, uncomment this line: +#PublishServerDescriptor 0 + +User tor diff --git a/tor/js/module.js b/tor/js/module.js new file mode 100755 index 0000000..4b46001 --- /dev/null +++ b/tor/js/module.js @@ -0,0 +1,156 @@ +registerController('tor_DependenciesController', ['$api', '$scope', '$rootScope', '$interval', '$timeout', function($api, $scope, $rootScope, $interval, $timeout) { + $scope.status = "Loading..."; + $scope.statusLabel = "default"; + $scope.starting = false; + + $scope.install = "Loading..."; + $scope.installLabel = "default"; + $scope.processing = false; + + $scope.saveSettingsLabel = "default"; + + $scope.device = ''; + $scope.sdAvailable = false; + + $rootScope.status = { + installed : false + }; + + $scope.refreshStatus = (function() { + $api.request({ + module: "tor", + action: "refreshStatus" + }, function(response) { + $scope.status = response.status; + $scope.statusLabel = response.statusLabel; + + $rootScope.status.installed = response.installed; + $scope.device = response.device; + $scope.sdAvailable = response.sdAvailable; + if(response.processing) $scope.processing = true; + $scope.install = response.install; + $scope.installLabel = response.installLabel; + }) + }); + + $scope.toggletor = (function() { + if($scope.status != "Stop") + $scope.status = "Starting..."; + else + $scope.status = "Stopping..."; + + $scope.statusLabel = "warning"; + $scope.starting = true; + + $rootScope.status.refreshOutput = false; + $rootScope.status.refreshHistory = false; + + $api.request({ + module: 'tor', + action: 'toggletor' + }, function(response) { + $timeout(function(){ + $scope.starting = false; + $scope.refreshStatus(); + }, 2000); + }) + }); + + $scope.handleDependencies = (function(param) { + if(!$rootScope.status.installed) + $scope.install = "Installing..."; + else + $scope.install = "Removing..."; + + $api.request({ + module: 'tor', + action: 'handleDependencies', + destination: param + }, function(response){ + if (response.success === true) { + $scope.installLabel = "warning"; + $scope.processing = true; + + $scope.handleDependenciesInterval = $interval(function(){ + $api.request({ + module: 'tor', + action: 'handleDependenciesStatus' + }, function(response) { + if (response.success === true){ + $scope.processing = false; + $interval.cancel($scope.handleDependenciesInterval); + $scope.refreshStatus(); + } + }); + }, 5000); + } + }); + }); + $scope.refreshStatus(); +}]); + +registerController('tor_ConfigurationController', ['$api', '$scope', '$rootScope', '$interval', '$timeout', function($api, $scope, $rootScope, $interval, $timeout) { + + $scope.refreshHiddenServices = (function() { + $api.request({ + module: 'tor', + action: 'refreshHiddenServices' + }, function(response) { + $scope.hiddenServices = response.hiddenServices; + }); + }); + + $scope.addHiddenService = (function() { + $api.request({ + module: 'tor', + action: 'addHiddenService', + name: $scope.name + }, function(response){ + $scope.refreshHiddenServices(); + }); + }); + + $scope.removeHiddenService = (function(name) { + $api.request({ + module: 'tor', + action: 'removeHiddenService', + name: name + }, function(response) { + $scope.refreshHiddenServices(); + }); + }); + + $scope.addServiceForward = (function() { + $api.request({ + module: 'tor', + action: 'addServiceForward', + name: $scope.name, + port: $scope.port, + redirect_to: $scope.redirect_to + }, function(response) { + $scope.hiddenServicesLoad = '(reloading...)'; + $timeout(function() { + $scope.hiddenServicesLoad = ''; + $scope.refreshHiddenServices(); + }, 2000); + }); + }); + + $scope.removeServiceForward = (function(name, port, redirect_to) { + $api.request({ + module: 'tor', + action: 'removeServiceForward', + name: name, + port: port, + redirect_to: redirect_to + }, function(response) { + $scope.hiddenServicesLoad = '(reloading...)'; + $timeout(function() { + $scope.hiddenServicesLoad = ''; + $scope.refreshHiddenServices(); + }, 2000); + }); + }); + + $scope.refreshHiddenServices(); +}]); diff --git a/tor/module.html b/tor/module.html new file mode 100755 index 0000000..2ce0d20 --- /dev/null +++ b/tor/module.html @@ -0,0 +1,158 @@ +
+
+
+
+

Dependencies

+
+
+ + + + + + + + + + +
Dependencies + + + +
tor + +
+
+ + + + +
+
+
+
+
+
+
+

Hidden Services {{ hiddenServicesLoad }}

+ {{ config }} +
+ +
+ + +
+

+ {{ service.name }} + ({{service.hostname}}) + + + +

+
+ + + + + + + + + +
PortRedirect to
{{ forward.port }}{{ forward.redirect_to }}
+
+
+
+ + + +
+
diff --git a/tor/module.info b/tor/module.info new file mode 100755 index 0000000..ccac56e --- /dev/null +++ b/tor/module.info @@ -0,0 +1,7 @@ +{ + "author":"catatonicprime", + "description":"Connect device to tor network, manage hidden services, etc.", + "devices": [ "nano", "tetra" ], + "title":"tor", + "version":"1.0" +} diff --git a/tor/scripts/dependencies.sh b/tor/scripts/dependencies.sh new file mode 100755 index 0000000..e2bd6fc --- /dev/null +++ b/tor/scripts/dependencies.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# author: catatonicprime +# date: March 2018 + +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/sd/lib:/sd/usr/lib +export PATH=$PATH:/sd/usr/bin:/sd/usr/sbin + +touch /tmp/tor.progress + +if [ "$1" = "install" ]; then + opkg update + if [ "$2" = "internal" ]; then + opkg install tor-geoip tor + elif [ "$2" = "sd" ]; then + opkg install tor-geoip tor --dest sd + fi + mkdir -p /etc/config/tor/ + cp /pineapple/modules/tor/files/torrc /etc/config/tor + mkdir -p /etc/config/tor/services + chown tor:tor /etc/config/tor/services + chown root:tor /etc/tor/torrc + chmod g+r /etc/tor/torrc +elif [ "$1" = "remove" ]; then + opkg remove tor-geoip tor + sed -i '/tor\/scripts\/autostart_tor.sh/d' /etc/rc.local + rm -rf /etc/config/tor +fi + +rm /tmp/tor.progress