spior 0.1.6 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+ require 'fileutils'
5
+ require 'nomansland'
6
+
7
+ module Spior
8
+ module Iptables
9
+ # Iptables::Rules, used to save or restore iptables rules
10
+ class Rules
11
+ def initialize
12
+ @tmp_iptables_rules = Tempfile.new('iptables_rules')
13
+ @tmp_spior_rules = Tempfile.new('spior_rules')
14
+ @save_path = search_iptables_config
15
+ end
16
+
17
+ def save
18
+ save_rules(@tmp_iptables_rules)
19
+ insert_comment(@tmp_spior_rules, @tmp_iptables_rules)
20
+ create_file(@tmp_spior_rules, @save_path)
21
+ Msg.p "Iptables rules saved at #{@save_path}"
22
+ end
23
+
24
+ def restore
25
+ return if restoring_older_rules(@save_path)
26
+
27
+ Msg.p 'Adding clearnet navigation...'
28
+ Iptables::Default.new.run!
29
+ end
30
+
31
+ protected
32
+
33
+ def save_rules(tmp_file)
34
+ Msg.p 'Saving Iptables rules...'
35
+ Helpers::Exec.new('iptables-save').run("> #{tmp_file.path}")
36
+ end
37
+
38
+ def insert_comment(spior_file, iptable_file)
39
+ outfile = File.open(spior_file.path, 'w')
40
+ outfile.puts '# Rules saved by Spior.'
41
+ outfile.puts(File.read(iptable_file.path))
42
+ outfile.close
43
+ end
44
+
45
+ def search_for_comment(filename)
46
+ File.open(filename) do |f|
47
+ f.each do |line|
48
+ return true if line.match(/saved by Spior/)
49
+ end
50
+ end
51
+ false
52
+ end
53
+
54
+ def move(src, dest)
55
+ if Process::Sys.getuid == '0'
56
+ FileUtils.mv(src, dest)
57
+ else
58
+ Helpers::Exec.new('mv').run("#{src} #{dest}")
59
+ end
60
+ end
61
+
62
+ def create_file(tmpfile, dest)
63
+ if File.exist? dest
64
+ if search_for_comment(dest)
65
+ Msg.p "Older Spior rules found #{dest}, erasing..."
66
+ else
67
+ Msg.p "File exist #{dest}, create backup #{dest}-backup..."
68
+ move(dest, "#{dest}-backup")
69
+ end
70
+ end
71
+ move(tmpfile.path, dest)
72
+ end
73
+
74
+ def restoring_older_rules(filename)
75
+ files = %W[#{filename}-backup #{filename}]
76
+ files.each do |f|
77
+ next unless File.exist?(f) || search_for_comment(f)
78
+
79
+ Iptables::Root.new.stop!
80
+ Msg.p "Found older rules #{f}, restoring..."
81
+ Helpers::Exec.new('iptables-restore').run(f)
82
+ return true
83
+ end
84
+ false
85
+ end
86
+
87
+ private
88
+
89
+ def search_iptables_config
90
+ case Nomansland.distro?
91
+ when :archlinux || :void
92
+ '/etc/iptables/iptables.rules'
93
+ when :debian
94
+ '/etc/iptables.up.rules'
95
+ when :gentoo
96
+ '/var/lib/iptables/rules-save'
97
+ else
98
+ Msg.report 'I don`t know where you distro save the rules for iptables yet'
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -1,58 +1,59 @@
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"
35
- @non_tor.each { |ip|
36
- ipt "-t #{table} -A OUTPUT -d #{ip} -j #{target}"
37
- }
37
+ target = 'RETURN' if table == 'nat'
38
+ @non_tor.each { |ip| ipt "-t #{table} -A OUTPUT -d #{ip} -j #{target}" }
38
39
 
39
- target = "REDIRECT --to-ports #{@tor.trans_port}" if table == "nat"
40
+ target = "REDIRECT --to-ports #{CONFIG.trans_port}" if table == 'nat'
40
41
  ipt "-t #{table} -A OUTPUT -p tcp -j #{target}"
41
- }
42
+ end
42
43
  end
43
44
 
44
45
  def input
45
46
  # SSH
46
- ipt "-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT"
47
+ ipt '-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT'
47
48
  # Allow loopback
48
49
  ipt "-A INPUT -i #{@lo} -j ACCEPT"
49
50
  # Accept related
50
- ipt "-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT"
51
+ ipt '-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT'
51
52
  end
52
53
 
53
54
  def all
54
- ipt "-t filter -A OUTPUT -p udp -j REJECT"
55
- ipt "-t filter -A OUTPUT -p icmp -j REJECT"
55
+ ipt '-t filter -A OUTPUT -p udp -j REJECT'
56
+ ipt '-t filter -A OUTPUT -p icmp -j REJECT'
56
57
  end
57
58
  end
58
59
  end
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Spior
4
+ # Interact with iptables
2
5
  module Iptables
3
6
  end
4
7
  end
@@ -6,3 +9,4 @@ end
6
9
  require_relative 'iptables/root'
7
10
  require_relative 'iptables/tor'
8
11
  require_relative 'iptables/default'
12
+ require_relative 'iptables/rules'
data/lib/spior/ipv6.rb ADDED
@@ -0,0 +1,35 @@
1
+ # lib/ipv6.rb
2
+ # frozen_string_literal: true
3
+
4
+ require 'auth'
5
+
6
+ module Spior
7
+ # Block or Allow ipv6 traffic with sysctl
8
+ class Ipv6
9
+ def initialize
10
+ @changed = false
11
+ end
12
+
13
+ def allow
14
+ apply_option('net.ipv6.conf.all.disable_ipv6', '0')
15
+ apply_option('net.ipv6.conf.default.disable_ipv6', '0')
16
+ Msg.p 'ipv6 allowed' if @changed
17
+ end
18
+
19
+ def block
20
+ apply_option('net.ipv6.conf.all.disable_ipv6', '1')
21
+ apply_option('net.ipv6.conf.default.disable_ipv6', '1')
22
+ Msg.p 'ipv6 blocked' if @changed
23
+ end
24
+
25
+ private
26
+
27
+ def apply_option(flag, value)
28
+ flag_path = flag.gsub('.', '/')
29
+ return unless File.exist?("/proc/sys/#{flag_path}")
30
+
31
+ Auth.new.sysctl(flag, value)
32
+ @changed = true
33
+ end
34
+ end
35
+ end
data/lib/spior/menu.rb CHANGED
@@ -1,43 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Spior
4
+ # Build an interactive menu for spior
2
5
  module Menu
3
- extend self
4
-
5
- def run
6
- banner
6
+ def self.run
7
7
  loop do
8
8
  Msg.head
9
- puts %q{Please select an option:
9
+ puts 'Please select an option:
10
10
 
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}
11
+ 1. Redirect traffic through Tor
12
+ 2. Reload Spior and change your IP
13
+ 3. Stop Tor and use a clearnet navigation
14
+ 4. Check info on your current IP
15
+ 5. Install all the dependencies
16
+ 6. Quit'
16
17
 
17
18
  puts
18
- print ">> "
19
+ print '>> '
19
20
  case gets.chomp
20
21
  when '1'
21
- Spior::Iptables::Tor.new.run!
22
+ Service.start
22
23
  when '2'
23
- Spior::Serice.restart
24
+ Service.restart
24
25
  when '3'
25
- Spior::Clear.all
26
+ Service.stop
26
27
  when '4'
27
- Spior::Status.info
28
+ Status.info
28
29
  when '5'
30
+ Dep.looking
31
+ else
29
32
  exit
30
33
  end
31
34
  end
32
35
  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
36
  end
43
37
  end
data/lib/spior/msg.rb CHANGED
@@ -1,28 +1,50 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rainbow'
2
4
 
5
+ # Used to display various message
3
6
  module Msg
4
- extend self
7
+ module_function
8
+
9
+ def banner
10
+ puts
11
+ puts '┏━┓┏━┓╻┏━┓┏━┓'
12
+ puts '┗━┓┣━┛┃┃ ┃┣┳┛'
13
+ puts '┗━┛╹ ╹┗━┛╹┗╸'
14
+ puts
15
+ # generated with toilet -F crop -f future spior
16
+ end
5
17
 
6
18
  def head
7
- puts Rainbow("------------------------------------------------").cyan
19
+ puts Rainbow('------------------------------------------------').cyan
8
20
  end
9
21
 
10
22
  def p(text)
11
- puts Rainbow("[").cyan + Rainbow("+").white + Rainbow("]").cyan + " " + text
23
+ opn = Rainbow('[').cyan
24
+ msg = Rainbow('+').white
25
+ cls = Rainbow(']').cyan
26
+ puts "#{opn}#{msg}#{cls} #{text}"
12
27
  end
13
28
 
14
29
  def err(text)
15
- puts Rainbow("[").red + Rainbow("-").white + Rainbow("]").red + " " + text
30
+ opn = Rainbow('[').red
31
+ msg = Rainbow('-').white
32
+ cls = Rainbow(']').red
33
+ puts "#{opn}#{msg}#{cls} #{text}"
16
34
  end
17
35
 
18
36
  def info(text)
19
- puts Rainbow("-").blue + Rainbow("-").white + Rainbow("-").blue + " " + text + " " + Rainbow("-").blue + Rainbow("-").white + Rainbow("-").blue
37
+ one = Rainbow('_').blue
38
+ two = Rainbow('-').white
39
+ thr = Rainbow('_').blue
40
+ puts "#{one}#{two}#{thr} #{text} #{one}#{two}#{thr}"
20
41
  end
21
42
 
22
43
  def report(text)
23
- puts ""
44
+ puts
24
45
  info text
25
- puts "Please, report this issue at https://github.com/szorfein/spior/issues"
26
- puts ""
46
+ puts 'Please, report this issue at https://github.com/szorfein/spior/issues'
47
+ puts
48
+ exit 1
27
49
  end
28
50
  end
data/lib/spior/options.rb CHANGED
@@ -1,13 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
4
 
3
5
  module Spior
6
+ # Options for the CLI
4
7
  class Options
5
- attr_reader :install , :tor , :persist
6
-
7
8
  def initialize(argv)
8
- @install = false
9
- @tor = false
10
- @persist = false
11
9
  parse(argv)
12
10
  end
13
11
 
@@ -15,46 +13,46 @@ module Spior
15
13
 
16
14
  def parse(argv)
17
15
  OptionParser.new do |opts|
18
- opts.on("-i", "--install", "Install the dependencies") do
19
- @install = true
16
+ opts.on('-i', '--install', 'Install the dependencies.') do
17
+ Dep.looking
20
18
  end
21
19
 
22
- opts.on("-t", "--tor", "Redirect traffic through TOR") do
23
- @tor = true
20
+ opts.on('-t', '--tor', 'Redirect traffic through TOR.') do
21
+ Service.start
24
22
  end
25
23
 
26
- opts.on("-r", "--reload", "Reload TOR to change your ip") do
27
- Spior::Service.restart
24
+ opts.on('-r', '--reload', 'Reload TOR to change your IP.') do
25
+ Service.restart
28
26
  exit
29
27
  end
30
28
 
31
- opts.on("-c", "--clearnet", "Reset iptables and return to clearnet navigation") do
32
- Spior::Clear.all
29
+ opts.on('-c', '--clearnet', 'Reset iptables and return to clearnet navigation.') do
30
+ Service.stop
33
31
  end
34
32
 
35
- opts.on("-s", "--status", "Look infos about your current ip") do
36
- Spior::Status.info
33
+ opts.on('-s', '--status', 'Look infos about your current IP.') do
34
+ Status.info
37
35
  exit
38
36
  end
39
37
 
40
- opts.on("-p", "--persist", "Active Spior at every boot.") do
41
- @persist = true
38
+ opts.on('-p', '--persist', 'Active Spior at every boot.') do
39
+ Service::Enable.new
42
40
  end
43
41
 
44
- opts.on("-m", "--menu", "Display an interactive menu") do
45
- Spior::Menu.run
42
+ opts.on('-m', '--menu', 'Display an interactive menu.') do
43
+ Menu.run
46
44
  end
47
45
 
48
- opts.on("-h", "--help", "Show this message") do
46
+ opts.on('-h', '--help', 'Show this message.') do
49
47
  puts opts
50
48
  exit
51
49
  end
52
50
 
53
51
  begin
54
- argv = ["-m"] if argv.empty?
52
+ argv = ['-m'] if argv.empty?
55
53
  opts.parse!(argv)
56
54
  rescue OptionParser::ParseError => e
57
- STDERR.puts e.message, "\n", opts
55
+ warn e.message, "\n", opts
58
56
  exit(-1)
59
57
  end
60
58
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nomansland'
4
+
5
+ module Spior
6
+ # Service make Spior persistent using services on system like iptables and tor
7
+ module Service
8
+ # Enable the Tor redirection when you boot your system
9
+ #
10
+ # It should use and enable the services:
11
+ # + tor
12
+ # + iptables
13
+ class Enable
14
+ def initialize
15
+ case Nomansland.distro?
16
+ when :gentoo
17
+ for_gentoo
18
+ when :archlinux
19
+ for_arch
20
+ else
21
+ Msg.report 'Your distro is not yet supported.'
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ def for_gentoo
28
+ Iptables::Rules.new.save
29
+ case Nomansland.init?
30
+ when :systemd
31
+ systemd_enable('iptables-restore', 'tor')
32
+ when :openrc
33
+ rc_upd = Helpers::Exec.new('rc-update')
34
+ rc_upd.run('rc-update add iptables boot')
35
+ rc_upd.run('rc-update add tor')
36
+ rc_upd.run('rc-update add tor default')
37
+ else
38
+ Msg.report 'Init no yet supported for start Iptables at boot'
39
+ end
40
+ end
41
+
42
+ def for_arch
43
+ Iptables::Rules.new.save
44
+ Tor::Config.new(Tempfile.new('torrc')).backup
45
+ systemd_enable('iptables', 'tor')
46
+ Msg.p 'Services enabled for Archlinux...'
47
+ end
48
+
49
+ private
50
+
51
+ def systemd_enable(*services)
52
+ systemctl = Helpers::Exec.new('systemctl')
53
+ services.each do |s|
54
+ Msg.p "Search for service #{s}..."
55
+ systemctl.run("enable #{s}") unless system("systemctl is-enabled #{s}")
56
+ end
57
+ end
58
+
59
+ def systemd_start(service)
60
+ systemctl = Helpers::Exec.new('systemctl')
61
+ Msg.p "Search for service #{service}..."
62
+ systemctl.run("start #{service}") unless system("systemctl is-active #{service}")
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,21 +1,14 @@
1
- require 'tty-which'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Spior
4
+ # Interact with Spior::Tor and Spior::Iptables
4
5
  module Service
5
6
  module_function
6
7
 
7
8
  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
9
+ Service.stop(clean: false)
10
+ Service.start
11
+ Msg.p 'ip changed.'
19
12
  end
20
13
  end
21
14
  end
@@ -1,26 +1,16 @@
1
- require 'tty-which'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Spior
4
+ # Interact with Spior::Tor and Spior::Iptables
4
5
  module Service
5
6
  module_function
6
7
 
8
+ # Service.start should start Tor if not alrealy running
9
+ # And start to redirect the local traffic with Iptables
7
10
  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
11
+ Tor::Start.new
12
+ Iptables::Tor.new.run!
13
+ Ipv6.new.block
24
14
  end
25
15
  end
26
16
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spior
4
+ # Interact with Spior::Tor and Spior::Iptables
5
+ module Service
6
+ module_function
7
+
8
+ def stop(clean: true)
9
+ Tor::Stop.new
10
+ Iptables::Rules.new.restore if clean
11
+ Ipv6.new.allow if clean
12
+ end
13
+ end
14
+ 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 => e
21
+ res = e.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 => e
42
+ res = e.io
43
+ puts "Fail to join server #{res.status}"
36
44
  end
37
45
  end
38
46
  end