spior 0.1.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,58 +1,61 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Spior
2
4
  module Iptables
5
+ # Make Local Redirection Through Tor.
3
6
  class Tor < Iptables::Root
4
7
  def initialize
5
8
  super
6
- @tor = Spior::Tor::Info.new
7
- @non_tor = ["#{@lo_addr}/8", "192.168.0.0/16", "172.16.0.0/12", "10.0.0.0/8"]
8
- @tables = ["nat", "filter"]
9
+ @non_tor = %W[#{@lo_addr}/8 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8]
10
+ @tables = %w[nat filter]
9
11
  end
10
12
 
11
13
  private
12
14
 
13
15
  def redirect
14
- @tables.each { |table|
15
- target = "ACCEPT"
16
- target = "RETURN" if table == "nat"
16
+ Msg.p 'Redirecting local traffic though Tor...'
17
+ @tables.map do |table|
18
+ target = 'ACCEPT'
19
+ target = 'RETURN' if table == 'nat'
17
20
 
18
21
  ipt "-t #{table} -F OUTPUT"
19
22
  ipt "-t #{table} -A OUTPUT -m state --state ESTABLISHED -j #{target}"
20
- ipt "-t #{table} -A OUTPUT -m owner --uid #{@tor.uid} -j #{target}"
23
+ ipt "-t #{table} -A OUTPUT -m owner --uid #{CONFIG.uid} -j #{target}"
21
24
 
22
- match_dns_port = @tor.dns
23
- if table == "nat"
24
- target = "REDIRECT --to-ports #{@tor.dns}"
25
- match_dns_port = "53"
25
+ match_dns_port = CONFIG.dns_port
26
+ if table == 'nat'
27
+ target = "REDIRECT --to-ports #{CONFIG.dns_port}"
28
+ match_dns_port = '53'
26
29
  end
27
30
 
28
31
  ipt "-t #{table} -A OUTPUT -p udp --dport #{match_dns_port} -j #{target}"
29
32
  ipt "-t #{table} -A OUTPUT -p tcp --dport #{match_dns_port} -j #{target}"
30
33
 
31
- target = "REDIRECT --to-ports #{@tor.trans_port}" if table == "nat"
32
- ipt "-t #{table} -A OUTPUT -d #{@tor.virt_addr} -p tcp -j #{target}"
34
+ target = "REDIRECT --to-ports #{CONFIG.trans_port}" if table == 'nat'
35
+ ipt "-t #{table} -A OUTPUT -d #{CONFIG.virt_addr} -p tcp -j #{target}"
33
36
 
34
- target = "RETURN" if table == "nat"
37
+ target = 'RETURN' if table == 'nat'
35
38
  @non_tor.each { |ip|
36
39
  ipt "-t #{table} -A OUTPUT -d #{ip} -j #{target}"
37
40
  }
38
41
 
39
- target = "REDIRECT --to-ports #{@tor.trans_port}" if table == "nat"
42
+ target = "REDIRECT --to-ports #{CONFIG.trans_port}" if table == 'nat'
40
43
  ipt "-t #{table} -A OUTPUT -p tcp -j #{target}"
41
- }
44
+ end
42
45
  end
43
46
 
44
47
  def input
45
48
  # SSH
46
- ipt "-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT"
49
+ ipt '-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT'
47
50
  # Allow loopback
48
51
  ipt "-A INPUT -i #{@lo} -j ACCEPT"
49
52
  # Accept related
50
- ipt "-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT"
53
+ ipt '-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT'
51
54
  end
52
55
 
53
56
  def all
54
- ipt "-t filter -A OUTPUT -p udp -j REJECT"
55
- ipt "-t filter -A OUTPUT -p icmp -j REJECT"
57
+ ipt '-t filter -A OUTPUT -p udp -j REJECT'
58
+ ipt '-t filter -A OUTPUT -p icmp -j REJECT'
56
59
  end
57
60
  end
58
61
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Spior
2
4
  module Iptables
3
5
  end
@@ -6,3 +8,4 @@ end
6
8
  require_relative 'iptables/root'
7
9
  require_relative 'iptables/tor'
8
10
  require_relative 'iptables/default'
11
+ require_relative 'iptables/rules'
data/lib/spior/menu.rb CHANGED
@@ -1,43 +1,36 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Spior
2
4
  module Menu
3
- extend self
4
-
5
- def run
6
- banner
5
+ def self.run
7
6
  loop do
8
7
  Msg.head
9
- puts %q{Please select an option:
8
+ puts 'Please select an option:
10
9
 
11
- 1. Redirect traffic through tor
12
- 2. Reload tor and change your ip
13
- 3. Clear and restore your files
14
- 4. Check info on your current ip
15
- 5. Quit}
10
+ 1. Redirect traffic through Tor
11
+ 2. Reload Spior and change your IP
12
+ 3. Stop Tor and use a clearnet navigation
13
+ 4. Check info on your current IP
14
+ 5. Install all the dependencies
15
+ 6. Quit'
16
16
 
17
17
  puts
18
- print ">> "
18
+ print '>> '
19
19
  case gets.chomp
20
20
  when '1'
21
- Spior::Iptables::Tor.new.run!
21
+ Spior::Service.start
22
22
  when '2'
23
- Spior::Serice.restart
23
+ Spior::Service.restart
24
24
  when '3'
25
- Spior::Clear.all
25
+ Spior::Service.stop
26
26
  when '4'
27
27
  Spior::Status.info
28
28
  when '5'
29
+ Spior::Dep.looking
30
+ else
29
31
  exit
30
32
  end
31
33
  end
32
34
  end
33
-
34
- private
35
-
36
- def banner
37
- puts "┏━┓┏━┓╻┏━┓┏━┓"
38
- puts "┗━┓┣━┛┃┃ ┃┣┳┛"
39
- puts "┗━┛╹ ╹┗━┛╹┗╸"
40
- # generated with toilet -F crop -f future spior
41
- end
42
35
  end
43
36
  end
data/lib/spior/msg.rb CHANGED
@@ -1,28 +1,42 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rainbow'
2
4
 
3
5
  module Msg
4
- extend self
6
+ module_function
7
+
8
+ def banner
9
+ puts
10
+ puts '┏━┓┏━┓╻┏━┓┏━┓'
11
+ puts '┗━┓┣━┛┃┃ ┃┣┳┛'
12
+ puts '┗━┛╹ ╹┗━┛╹┗╸'
13
+ puts
14
+ # generated with toilet -F crop -f future spior
15
+ end
5
16
 
6
17
  def head
7
- puts Rainbow("------------------------------------------------").cyan
18
+ puts Rainbow('------------------------------------------------').cyan
8
19
  end
9
20
 
10
21
  def p(text)
11
- puts Rainbow("[").cyan + Rainbow("+").white + Rainbow("]").cyan + " " + text
22
+ puts Rainbow('[').cyan + Rainbow('+').white + Rainbow(']').cyan + ' ' + text
12
23
  end
13
24
 
14
25
  def err(text)
15
- puts Rainbow("[").red + Rainbow("-").white + Rainbow("]").red + " " + text
26
+ puts Rainbow('[').red + Rainbow('-').white + Rainbow(']').red + ' ' + text
16
27
  end
17
28
 
18
29
  def info(text)
19
- puts Rainbow("-").blue + Rainbow("-").white + Rainbow("-").blue + " " + text + " " + Rainbow("-").blue + Rainbow("-").white + Rainbow("-").blue
30
+ print Rainbow('-').blue + Rainbow('-').white + Rainbow('-').blue
31
+ print " #{text} "
32
+ print Rainbow('-').blue + Rainbow('-').white + Rainbow('-').blue + "\n"
20
33
  end
21
34
 
22
35
  def report(text)
23
- puts ""
36
+ puts
24
37
  info text
25
- puts "Please, report this issue at https://github.com/szorfein/spior/issues"
26
- puts ""
38
+ puts 'Please, report this issue at https://github.com/szorfein/spior/issues'
39
+ puts
40
+ exit 1
27
41
  end
28
42
  end
data/lib/spior/options.rb CHANGED
@@ -1,13 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
4
 
3
5
  module Spior
4
6
  class Options
5
- attr_reader :install , :tor , :persist
6
-
7
7
  def initialize(argv)
8
- @install = false
9
- @tor = false
10
- @persist = false
11
8
  parse(argv)
12
9
  end
13
10
 
@@ -15,46 +12,46 @@ module Spior
15
12
 
16
13
  def parse(argv)
17
14
  OptionParser.new do |opts|
18
- opts.on("-i", "--install", "Install the dependencies") do
19
- @install = true
15
+ opts.on('-i', '--install', 'Install the dependencies.') do
16
+ Spior::Dep.looking
20
17
  end
21
18
 
22
- opts.on("-t", "--tor", "Redirect traffic through TOR") do
23
- @tor = true
19
+ opts.on('-t', '--tor', 'Redirect traffic through TOR.') do
20
+ Spior::Service.start
24
21
  end
25
22
 
26
- opts.on("-r", "--reload", "Reload TOR to change your ip") do
23
+ opts.on('-r', '--reload', 'Reload TOR to change your IP.') do
27
24
  Spior::Service.restart
28
25
  exit
29
26
  end
30
27
 
31
- opts.on("-c", "--clearnet", "Reset iptables and return to clearnet navigation") do
32
- Spior::Clear.all
28
+ opts.on('-c', '--clearnet', 'Reset iptables and return to clearnet navigation.') do
29
+ Spior::Service.stop
33
30
  end
34
31
 
35
- opts.on("-s", "--status", "Look infos about your current ip") do
32
+ opts.on('-s', '--status', 'Look infos about your current IP.') do
36
33
  Spior::Status.info
37
34
  exit
38
35
  end
39
36
 
40
- opts.on("-p", "--persist", "Active Spior at every boot.") do
41
- @persist = true
37
+ opts.on('-p', '--persist', 'Active Spior at every boot.') do
38
+ Spior::Service.enable
42
39
  end
43
40
 
44
- opts.on("-m", "--menu", "Display an interactive menu") do
41
+ opts.on('-m', '--menu', 'Display an interactive menu.') do
45
42
  Spior::Menu.run
46
43
  end
47
44
 
48
- opts.on("-h", "--help", "Show this message") do
45
+ opts.on('-h', '--help', 'Show this message.') do
49
46
  puts opts
50
47
  exit
51
48
  end
52
49
 
53
50
  begin
54
- argv = ["-m"] if argv.empty?
51
+ argv = ['-m'] if argv.empty?
55
52
  opts.parse!(argv)
56
53
  rescue OptionParser::ParseError => e
57
- STDERR.puts e.message, "\n", opts
54
+ warn e.message, "\n", opts
58
55
  exit(-1)
59
56
  end
60
57
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nomansland'
4
+
5
+ module Spior
6
+ module Service
7
+ extend self
8
+
9
+ # enable the Tor redirection when you boot your system
10
+ #
11
+ # It should use and enable the services:
12
+ # + tor
13
+ # + iptables
14
+ def enable
15
+ case Nomansland.distro?
16
+ when :gentoo
17
+ for_gentoo
18
+ when :archlinux
19
+ Iptables::Rules.new.backup
20
+ Tor::Config.new(Tempfile.new('torrc')).backup
21
+ Helpers::Exec.new('systemctl').run('enable iptables tor')
22
+ Msg.p 'Services enabled for Archlinux...'
23
+ else
24
+ Msg.report 'Your distro is not yet supported.'
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def for_gentoo
31
+ case Nomansland.init?
32
+ when :systemd
33
+ systemd_start('iptables-store')
34
+ systemd_enable('iptables-restore')
35
+ systemd_enable('tor')
36
+ when :openrc
37
+ system('sudo /etc/init.d/iptables save')
38
+ rc_upd = Helpers::Exec.new('rc-update')
39
+ rc_upd.run('rc-update add iptables boot')
40
+ rc_upd.run('rc-update add tor')
41
+ rc_upd.run('rc-update add tor default')
42
+ else
43
+ Msg.report 'Init no yet supported for start Iptables at boot'
44
+ end
45
+ end
46
+
47
+ def systemd_enable(service)
48
+ systemctl = Helpers::Exec.new('systemctl')
49
+ Msg.p "Search for service #{service}..."
50
+ unless system("systemctl is-enabled #{service}")
51
+ systemctl.run("enable #{service}")
52
+ end
53
+ end
54
+
55
+ def systemd_start(service)
56
+ systemctl = Helpers::Exec.new('systemctl')
57
+ Msg.p "Search for service #{service}..."
58
+ unless system("systemctl is-active #{service}")
59
+ systemctl.run("start #{service}")
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,21 +1,13 @@
1
- require 'tty-which'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Spior
4
4
  module Service
5
5
  module_function
6
6
 
7
7
  def restart
8
- if TTY::Which.exist?('systemctl')
9
- Helpers::Exec.new("systemctl").run("restart tor")
10
- Msg.p "ip changed."
11
- elsif TTY::Which.exist? 'sv'
12
- Helpers::Exec.new('sv').run('restart tor')
13
- Msg.p 'ip changed.'
14
- elsif File.exist? '/etc/init.d/tor'
15
- Helpers::Exec.new('/etc/init.d/tor').run('restart')
16
- else
17
- Msg.report "Don't known yet how to restart Tor for your system."
18
- end
8
+ Service.stop
9
+ Service.start
10
+ Msg.p 'ip changed.'
19
11
  end
20
12
  end
21
13
  end
@@ -1,26 +1,14 @@
1
- require 'tty-which'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Spior
4
4
  module Service
5
5
  module_function
6
6
 
7
+ # Service.start should start Tor if not alrealy running
8
+ # And start to redirect the local traffic with Iptables
7
9
  def start
8
- if TTY::Which.exist?('systemctl')
9
- state = `systemctl is-active tor`.chomp
10
- unless state == 'active'
11
- Helpers::Exec.new("systemctl").run("start tor")
12
- Msg.p "TOR started."
13
- end
14
- elsif TTY::Which.exist? 'sv'
15
- unless File.exist? '/var/service/tor'
16
- Helpers::Exec.new('ln').run('-s /etc/sv/tor /var/service/tor')
17
- Msg.p "TOR started."
18
- end
19
- elsif File.exist? '/etc/init.d/tor'
20
- Helpers::Exec.new('/etc/init.d/tor').run('start')
21
- else
22
- Msg.report "Don't known yet how to start Tor for your system."
23
- end
10
+ Tor.start
11
+ Iptables::Tor.new.run!
24
12
  end
25
13
  end
26
14
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spior
4
+ module Service
5
+ module_function
6
+
7
+ def stop
8
+ Tor.stop
9
+ Iptables::Rules.new.restore
10
+ end
11
+ end
12
+ end
data/lib/spior/service.rb CHANGED
@@ -1,7 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Spior
4
+ # Service should start/stop/restart Tor and Iptable.
2
5
  module Service
3
6
  end
4
7
  end
5
8
 
6
9
  require_relative 'service/start'
10
+ require_relative 'service/stop'
7
11
  require_relative 'service/restart'
12
+ require_relative 'service/enable'
data/lib/spior/status.rb CHANGED
@@ -1,38 +1,46 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'open-uri'
2
4
  require 'json'
3
5
 
4
6
  module Spior
7
+ # Status display information on your current IP addresse
8
+ #
9
+ # If you use an IPV6 address, it should fail to display a Tor IP...
5
10
  module Status
11
+ # Check on https://check.torproject.org/api/ip if Tor is enable or not
12
+ # and display the result.
6
13
  def self.enable
7
- begin
8
- status = "Disable"
9
- api_check = "https://check.torproject.org/api/ip"
10
- URI.open(api_check) do |l|
11
- hash = JSON.parse l.read
12
- status = "Enable" if hash["IsTor"] == true
13
- end
14
- status
15
- rescue OpenURI::HTTPError => error
16
- res = error.io
17
- puts "Fail to join server #{res.status}"
14
+ status = 'Disable'
15
+ URI.open('https://check.torproject.org/api/ip') do |l|
16
+ hash = JSON.parse l.read
17
+ status = 'Enable' if hash['IsTor'] == true
18
18
  end
19
+ status
20
+ rescue OpenURI::HTTPError => error
21
+ res = error.io
22
+ puts "Fail to join server #{res.status}"
19
23
  end
20
24
 
25
+ # info check and display information from https://ipleak.net/json
26
+ #
27
+ # Check for:
28
+ # * +ip+
29
+ # * +continent_name+
30
+ # * +time_zone+
31
+ #
32
+ # We can add later info on City/Region or other things.
21
33
  def self.info
22
- begin
23
- api_check = "https://ipleak.net/json"
24
- URI.open(api_check) do |l|
25
- hash = JSON.parse l.read
26
- puts
27
- puts " Current ip ===> #{hash["ip"]}"
28
- puts " Continent ===> #{hash["continent_name"]}"
29
- puts " Timezone ===> #{hash["time_zone"]}"
30
- end
31
- puts " Status ===> #{enable}"
32
- rescue OpenURI::HTTPError => error
33
- res = error.io
34
- puts "Fail to join server #{res.status}"
34
+ URI.open('https://ipleak.net/json') do |l|
35
+ hash = JSON.parse l.read
36
+ puts " Current ip ===> #{hash['ip']}"
37
+ puts " Continent ===> #{hash['continent_name']}"
38
+ puts " Timezone ===> #{hash['time_zone']}"
35
39
  end
40
+ puts " Status ===> #{enable}"
41
+ rescue OpenURI::HTTPError => error
42
+ res = error.io
43
+ puts "Fail to join server #{res.status}"
36
44
  end
37
45
  end
38
46
  end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+
5
+ module Spior
6
+ module Tor
7
+ # Generate a config file (torrc) for Spior
8
+ class Config
9
+ # ==== Attributes
10
+ #
11
+ # * +filename+ - A reference to a tempfile like filename=Tempfile.new('foo')
12
+ #
13
+ def initialize(filename)
14
+ @filename = filename
15
+ @content = ['# Generated by Spior, don\'t edit.', 'RunAsDaemon 1',
16
+ 'ClientOnly 1', 'SocksPort 0']
17
+ @content_torrc = []
18
+ end
19
+
20
+ # Generate a `torrc` compatible file for Spior
21
+ # Use value from Spior::CONFIG
22
+ def generate
23
+ generate_content(@content)
24
+ return if @content.length == 4
25
+
26
+ File.write @filename.path, @content.join("\n") + "\n"
27
+ Msg.p 'Generating Tor config...'
28
+ end
29
+
30
+ # Save current Tor options (Spior::CONFIG) in /etc/tor/torrc
31
+ # Only if theses options are not alrealy present
32
+ def backup
33
+ generate_content(@content_torrc)
34
+ outfile = File.open(@filename.path, 'w')
35
+ outfile.puts(File.read('/etc/tor/torrc'))
36
+ outfile.puts(@content_torrc.join("\n")) if @content_torrc != []
37
+ outfile.chmod(0644)
38
+ outfile.close
39
+
40
+ Msg.p 'Saving Tor options...'
41
+ move(@filename.path, '/etc/tor/torrc')
42
+ end
43
+
44
+ protected
45
+
46
+ def generate_content(content)
47
+ adding content, 'AutomapHostsOnResolve 1'
48
+ adding content, "DNSPort #{CONFIG.dns_port}"
49
+ adding content, "VirtualAddrNetworkIpv4 #{CONFIG.virt_addr}"
50
+ adding content, "TransPort #{CONFIG.trans_port} IsolateClientAddr
51
+ IsolateClientProtocol IsolateDestAddr IsolateDestPort"
52
+ end
53
+
54
+ private
55
+
56
+ def search(option_name)
57
+ File.open('/etc/tor/torrc') do |f|
58
+ f.each do |line|
59
+ return Regexp.last_match(1) if line.match(/#{option_name} ([a-z0-9]*)/i)
60
+ end
61
+ end
62
+ false
63
+ end
64
+
65
+ def adding(content, option)
66
+ o = option.split(' ')
67
+ all = o[1..o.length].join(' ')
68
+ unless search(o[0])
69
+ content << "#{o[0]} #{all}"
70
+ end
71
+ end
72
+
73
+ def digest_match?(src, dest)
74
+ md5_src = Digest::MD5.file src
75
+ md5_dest = Digest::MD5.file dest
76
+ md5_src == md5_dest
77
+ end
78
+
79
+ # Permission for Archlinux on a torrc are chmod 644, chown root:root
80
+ def fix_perm(file)
81
+ if Process::Sys.getuid == '0'
82
+ file.chown(0, 0)
83
+ else
84
+ Helpers::Exec.new('chown').run("root:root #{file}")
85
+ end
86
+ end
87
+
88
+ def move(src, dest)
89
+ return if digest_match? src, dest
90
+
91
+ fix_perm(@filename.path)
92
+ if Process::Sys.getuid == '0'
93
+ FileUtils.mv(src, dest)
94
+ else
95
+ Helpers::Exec.new('mv').run("#{src} #{dest}")
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nomansland'
4
+
5
+ module Spior
6
+ module Tor
7
+ ##
8
+ # Data
9
+ # Fill Spior::CONFIG with data found on `/etc/tor/torrc` or set default.
10
+ #
11
+ # ==== Attributes
12
+ #
13
+ # * +user+ - Username used by Tor on your distro, e.g 'tor' on Archlinux
14
+ # * +dns_port+ - Open this port to listen for UDP DNS requests, and resolve them anonymously
15
+ # * +uid+ - The uid value from the user attribute.
16
+ # * +trans_port+ - Port to open to listen for transparent proxy connections.
17
+ # * +virt_addr+ - Default use '10.192.0.0/10'.
18
+ #
19
+ class Data
20
+ attr_accessor :user, :dns_port, :trans_port, :virt_addr, :uid
21
+
22
+ def initialize
23
+ @user = search('User') || 'tor'
24
+ @dns_port = search('DNSPort') || '9061'
25
+ @trans_port = search('TransPort') || '9040'
26
+ @virt_addr = search('VirtualAddrNetworkIPv4') || '10.192.0.0/10'
27
+ @uid = search_uid || 0
28
+ end
29
+
30
+ private
31
+
32
+ # Search value of option_name in the /etc/tor/torrc
33
+ # Return false by default
34
+ def search(option_name)
35
+ File.open('/etc/tor/torrc') do |f|
36
+ f.each do |line|
37
+ return Regexp.last_match(1) if line.match(/#{option_name} ([a-z0-9.\/]*)/i)
38
+ end
39
+ end
40
+ false
41
+ end
42
+
43
+ def search_uid
44
+ case Nomansland.distro?
45
+ when :debian || :ubuntu
46
+ `id -u debian-tor`.chomp
47
+ else
48
+ `id -u #{@user}`.chomp
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end