nub 0.0.96 → 0.0.103

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +168 -4
  3. data/lib/nub/log.rb +25 -23
  4. data/lib/nub/net.rb +192 -31
  5. data/lib/nub/sys.rb +21 -0
  6. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a0f201649f1ad4a08b5350321b544e0188141e414cae039f9aa27ba3288ed4bf
4
- data.tar.gz: 7bd2ef167e448539838bc5c1040ece7f652b3e3713d55450eeec74a9a2fcec26
3
+ metadata.gz: e03ab8b435d9eccad62fe0d1a515377dddf39cdd752e6ae643dd4337bb3a606e
4
+ data.tar.gz: 4795f2c20f7eeaf1da565f41c783d15b20007307f3cd926bbe28860419511cdc
5
5
  SHA512:
6
- metadata.gz: a50433d69104c80b7fb77ca5ab1ce2402e15aa2d9269b5ee4fbef4e5c070dc337e36d37ce4be1eb4085caaac1bb7a3c606aeac7bde96f68cdecebdcf8ae2be71
7
- data.tar.gz: 3653bbcfb850ec55aa6da6773b56bf955ca03503dd7fbbef2b20ac79834f2d8c146eaa7d67a7d243faf7b686626de87cd42d42f92926901f77f65b81d9a64621
6
+ metadata.gz: 69825e1f9cb11e189c77ab8ca30f9668300759f30f7fa1805153994c5ffb24eb017b7a3f12abd2067cdf1b04110a64b3558c7ff220d1211172060985ea52f7fc
7
+ data.tar.gz: cbdb16ed3fb4eb6b9f219492d60279d6035c233d1c21802b745079ea3e1d78fe12e0610bcfc0cc9fcfcaf2c18925c2ceff61434df84727f562287e246566da6b
data/README.md CHANGED
@@ -8,7 +8,7 @@ Collection of ruby utils I've used in several of my projects and wanted re-usabl
8
8
 
9
9
  ### Table of Contents
10
10
  * [Deploy](#deploy)
11
- * [Commander](#commander)
11
+ * [Commander Module](#commander-module)
12
12
  * [Commands](#commands)
13
13
  * [Command Parameters ](#command-paramaters)
14
14
  * [Chained Commands](#chained-commands)
@@ -24,7 +24,22 @@ Collection of ruby utils I've used in several of my projects and wanted re-usabl
24
24
  * [Help](#help)
25
25
  * [Examples](#examples)
26
26
  * [Indicators](#indicators)
27
- * [Config](#config)
27
+ * [Config Module](#config-module)
28
+ * [Core Module](#core-module)
29
+ * [FileUtils Extensions](#fileutils-extensions)
30
+ * [Hash Module](#hash-module)
31
+ * [Log Module](#Log-module)
32
+ * [Module Extensions](#module-extensions)
33
+ * [Net Module](#net-module)
34
+ * [Network Namespaces](#network-namespaces)
35
+ * [Teamviewer Example](#teamviewer-example)
36
+ * [PIA VPN Example](#pia-vpn-example)
37
+ * [Network Proxy](#network-proxy)
38
+ * [Pacman Module](#pacman-module)
39
+ * [Process Module](#process-module)
40
+ * [Sys Module](#sys-module)
41
+ * [ThreadComm Module](#thread-comm-module)
42
+ * [User Module](#user-module)
28
43
  * [Ruby Gem Creation](#ruby-gem-creation)
29
44
  * [Package Layout](#package-layout)
30
45
  * [Build Gem](#build-gem)
@@ -37,7 +52,7 @@ Collection of ruby utils I've used in several of my projects and wanted re-usabl
37
52
  ## Deploy <a name="deploy"></a>
38
53
  Run: `bundle install --system`
39
54
 
40
- ## Commander <a name="commander"></a>
55
+ ## Commander Module <a name="commander-module"></a>
41
56
  Commander was created mainly because all available options parsers seemed overly complicated and
42
57
  overweight and partly because I enjoyed understanding every bit going into it. Commander offers
43
58
  ***git*** or ***kubectl*** like command syntax.
@@ -323,7 +338,7 @@ Usage: ./builder build [options]
323
338
  -h|--help Print command/options help
324
339
  ```
325
340
 
326
- ## Config <a name="config"></a>
341
+ ## Config Module <a name="config-module"></a>
327
342
  Config is a simple YAML wrapper with some extra features. Since it implements the ***Singleton***
328
343
  pattern you can easily use it through out your app without carrying around instances everywhere.
329
344
  It creates config files in memory and saves them to the config ***~/.config*** directory when saved
@@ -334,6 +349,155 @@ Initialize once on entry of your app and leverage throughout:
334
349
  Config.init("openvpn.yml")
335
350
  ```
336
351
 
352
+ ## Core Module <a name="core-module"></a>
353
+
354
+ ## FileUtils Extensions <a name="fileutils-module"></a>
355
+
356
+ ## Hash Module <a name="hash-module"></a>
357
+
358
+ ## Log Module <a name="log-module"></a>
359
+
360
+ ## Module Extensions <a name="module-extensions"></a>
361
+
362
+ ## Net Module <a name="net-module"></a>
363
+ The network module is a collection of network related helpers and automation to simplify tasks and
364
+ encapsulate functionality into reusable components.
365
+
366
+ ### Network Namespaces <a name="network-namespaces"></a>
367
+ Linux by default shares a single set of network interfaces and routing table entries, such that an
368
+ installed application can bind to all interfaces and has access to all other services currently
369
+ running on the system. Network namespaces implemented in the kernel provide a way to isolate
370
+ networks and services from each other. This is the technology that docker uses to isolate docker
371
+ apps from the host and other docker apps.
372
+
373
+ Network namespaces provide a way to have different separate virtual interfaces and routing tables
374
+ that operate independent of each other. They can be easily manipulated via the ***ip netns*** command.
375
+
376
+ ```bash
377
+ # Create a new network namespace
378
+ # ip netns add <namespace>
379
+ sudo ip netns add foo
380
+
381
+ # List network namespaces
382
+ ip netns list
383
+ ```
384
+
385
+ Once a network namespace has been created you need to configure how it connected to the host. This
386
+ can be done by creating a pair of virtual Ethernet ***veth*** interfaces and assigning them to the
387
+ new network namespace as by default they will be assigned to the root namespace. The root namespace
388
+ or global namespace is the default namespace used by the host for regular networking.
389
+
390
+ ```bash
391
+ # Create veth pair veth1 and veth2
392
+ sudo ip link add veth1 type veth peer name veth2
393
+
394
+ # Verify veth pair creation and view their relationship
395
+ # both are part of the root namespace by default
396
+ ip a
397
+ # veth1@veth2: <BROADCAST,MULTICAST,M-DOWN>
398
+ # veth2@veth1: <BROADCAST,MULTICAST,M-DOWN>
399
+
400
+ # Connect foo namespace to root namespace by assigning half the veth pair to the foo namespace
401
+ sudo ip link set veth2 netns foo
402
+
403
+ # Verify that the root namespace listing no longer shows veth2
404
+ # notice also that the relationship of veth1 has changed
405
+ ip a
406
+ # veth1@if4: <BROADCAST,MULTICAST>
407
+
408
+ # Verify that the foo namespace now owns veth2
409
+ sudo ip netns exec foo ip a
410
+ # lo: <LOOPBACK>
411
+ # veth2@if5: <BROADCAST,MULTICAST>
412
+
413
+ # Assign IPv4 address to the new veth pair
414
+ sudo ifconfig veth1 192.168.100.1/24 up
415
+ sudo ip netns exec foo ifconfig veth2 192.168.100.2/24 up
416
+
417
+ # Verify that the assigned ips took
418
+ ip a
419
+ # inet 192.168.100.1/24 brd 192.168.100.255 scope global veth1
420
+
421
+ sudo ip netns exec foo ip a
422
+ # inet 192.168.100.2/24 brd 192.168.100.255 scope global veth2
423
+
424
+ # Verify connectivity between veth pair
425
+ sudo ping 192.168.100.2
426
+ # 64 bytes from 192.168.100.2: icmp_seq=1 ttl=64 time=0.070 ms
427
+
428
+ sudo ip netns exec foo ping 192.168.100.1
429
+ # 64 bytes from 192.168.100.1: icmp_seq=1 ttl=64 time=0.080 ms
430
+ ```
431
+
432
+ Now we have connectivity between our veth pair across network namespaces. Because
433
+ ***192.168.100.0/24*** network, that we configured the veth pair on, is a separate network from the
434
+ host any applications running within the new network namespace will not have connectivity to
435
+ anything else on your host or networks currently including external access to the internet.
436
+
437
+ ```bash
438
+ # Prove out isolation
439
+ sudo ping www.google.com
440
+ # 64 bytes from -----.1e100.net (172.217.1.196): icmp_seq=1 ttl=56 time=13.3 ms
441
+
442
+ sudo ip netns exec foo ping www.google.com
443
+ # connect: Network is unreachable
444
+ ```
445
+
446
+ In the following sub sections I'll show you how to automated this compliated setup using the
447
+ ***Net*** ruby module.
448
+
449
+ #### TeamViewer Example <a name="teamviewer-example"></a>
450
+ In this example I'll be showing you how to isolate Teamviewer such that Teamviewer is only able to
451
+ bind to the veth2 IPv4 address that we create for it rather than all network interfaces on the host.
452
+ This will allow you to have Teamviewer running and accessible from your network facing IP but also
453
+ to be able to SSH port forward other Teamviewer instances to your loopback interface or other veth
454
+ addresses.
455
+
456
+ ```ruby
457
+ WIP
458
+ ```
459
+
460
+ #### PIA VPN Example <a name="pia-vpn-example"></a>
461
+ ```ruby
462
+ WIP
463
+
464
+ Example1: Network namespace with access only to the 192.168.100.0 network which only has the
465
+ host address 192.168.100.1 available which only has access to the internet via the PIA VPN.
466
+
467
+ namespace = 'pia'
468
+ host_veth = Veth.new('veth1', '192.168.100.1')
469
+ guest_veth = Veth.new('veth2', '192.168.100.2')
470
+ network = Network.new('192.168.100.0', '24', ['209.222.18.222', '209.222.18.218'])
471
+ ```
472
+
473
+ ### Network Proxy <a name="network-proxy"></a>
474
+ The Net module provides simple access to the system proxy environment variables.
475
+
476
+ ```ruby
477
+ Net.proxy.ftp
478
+ Net.proxy.http
479
+ Net.proxy.https
480
+ Net.proxy.no
481
+ Net.proxy.uri
482
+ Net.proxy.port
483
+
484
+ # Simple way to check if a proxy is set
485
+ Net.proxy?
486
+
487
+ # Bash compatible string to use to insert proxy into commands
488
+ Net.proxy_export
489
+ ```
490
+
491
+ ## Pacman Module <a name="pacman-module"></a>
492
+
493
+ ## Process Module <a name="process-module"></a>
494
+
495
+ ## Sys Module <a name="sys-module"></a>
496
+
497
+ ## ThreadComm Module <a name="threadcomm-module"></a>
498
+
499
+ ## User Module <a name="user-module"></a>
500
+
337
501
  ## Ruby Gem Creation <a name="ruby-gem-creation"></a>
338
502
  http://guides.rubygems.org/make-your-own-gem/
339
503
 
@@ -25,6 +25,7 @@ require 'ostruct'
25
25
  require 'colorize'
26
26
  require_relative 'sys'
27
27
  require_relative 'core'
28
+ require_relative 'module'
28
29
 
29
30
  LogLevel = OpenStruct.new({
30
31
  error: 0,
@@ -38,34 +39,32 @@ LogLevel = OpenStruct.new({
38
39
  # Uses Mutex.synchronize where required to provide thread safety.
39
40
  module Log
40
41
  extend self
41
- @@_level = 3
42
+ mattr_accessor(:id, :path, :level)
43
+
44
+ @@level = 3
45
+ @@path = nil
42
46
  @@_queue = nil
43
47
  @@_stdout = true
44
48
  @@_monitor = Monitor.new
45
49
 
46
- # Public properties
47
- class << self
48
- attr_reader(:id, :path)
49
- end
50
-
51
50
  # Singleton's init method can be called multiple times to reset.
52
51
  # @param path [String] path to log file
53
52
  # @param queue [Bool] use a queue as well
54
53
  # @param stdout [Bool] turn on or off stdout
55
54
  # @param level [LogLevel] level at which to log
56
55
  def init(path:nil, level:LogLevel.debug, queue:false, stdout:true)
57
- @id ||= 'singleton'.object_id
56
+ self.id ||= 'singleton'.object_id
57
+ self.path = path ? File.expand_path(path) : nil
58
+ self.level = level
58
59
 
59
- @path = path ? File.expand_path(path) : nil
60
- @@_level = level
61
60
  @@_queue = queue ? Queue.new : nil
62
61
  @@_stdout = stdout
63
62
  $stdout.sync = true
64
63
 
65
64
  # Open log file creating as needed
66
- if @path
67
- FileUtils.mkdir_p(File.dirname(@path)) if !File.exist?(File.dirname(@path))
68
- @file = File.open(@path, 'a')
65
+ if self.path
66
+ FileUtils.mkdir_p(File.dirname(self.path)) if !File.exist?(File.dirname(self.path))
67
+ @file = File.open(self.path, 'a')
69
68
  @file.sync = true
70
69
  end
71
70
  end
@@ -74,7 +73,7 @@ module Log
74
73
  def call_details
75
74
  @@_monitor.synchronize{
76
75
 
77
- # Skip first 3 on stack (i.e. 0 = block in call_details, 1 = synchronize, 2 = call_detail)
76
+ # Skip first 3 on stack (i.e. 0 = block in call_details, 1 = synchronize, 2 = call_detail)
78
77
  stack = caller_locations(3, 20)
79
78
 
80
79
  # Skip past any calls in 'log.rb' or 'monitor.rb'
@@ -124,7 +123,7 @@ module Log
124
123
 
125
124
  # Handle output
126
125
  if !str.empty?
127
- @file << str.strip_color if @path
126
+ @file << str.strip_color if self.path
128
127
  @@_queue << str if @@_queue
129
128
  $stdout.print(str) if @@_stdout
130
129
  end
@@ -154,7 +153,7 @@ module Log
154
153
  end
155
154
 
156
155
  # Handle output
157
- @file.puts(str.strip_color) if @path
156
+ @file.puts(str.strip_color) if self.path
158
157
  @@_queue << "#{str}\n" if @@_queue
159
158
  $stdout.puts(str) if @@_stdout
160
159
 
@@ -167,18 +166,19 @@ module Log
167
166
  opts = args.find{|x| x.is_a?(Hash)}
168
167
  opts[:loc] = true and opts[:type] = 'E' if opts
169
168
  args << {:loc => true, :type => 'E'} if !opts
170
-
171
- return self.puts(*args)
169
+ newline = (opts && opts.key?(:newline)) ? opts[:newline] : true
170
+ return newline ? self.puts(*args) : self.print(*args)
172
171
  }
173
172
  end
174
173
 
175
174
  def warn(*args)
176
175
  @@_monitor.synchronize{
177
- if LogLevel.warn <= @@_level
176
+ if LogLevel.warn <= self.level
178
177
  opts = args.find{|x| x.is_a?(Hash)}
179
178
  opts[:type] = 'W' if opts
180
179
  args << {:type => 'W'} if !opts
181
- return self.puts(*args)
180
+ newline = (opts && opts.key?(:newline)) ? opts[:newline] : true
181
+ return newline ? self.puts(*args) : self.print(*args)
182
182
  end
183
183
  return true
184
184
  }
@@ -186,11 +186,12 @@ module Log
186
186
 
187
187
  def info(*args)
188
188
  @@_monitor.synchronize{
189
- if LogLevel.info <= @@_level
189
+ if LogLevel.info <= self.level
190
190
  opts = args.find{|x| x.is_a?(Hash)}
191
191
  opts[:type] = 'I' if opts
192
192
  args << {:type => 'I'} if !opts
193
- return self.puts(*args)
193
+ newline = (opts && opts.key?(:newline)) ? opts[:newline] : true
194
+ return newline ? self.puts(*args) : self.print(*args)
194
195
  end
195
196
  return true
196
197
  }
@@ -198,11 +199,12 @@ module Log
198
199
 
199
200
  def debug(*args)
200
201
  @@_monitor.synchronize{
201
- if LogLevel.debug <= @@_level
202
+ if LogLevel.debug <= self.level
202
203
  opts = args.find{|x| x.is_a?(Hash)}
203
204
  opts[:type] = 'D' if opts
204
205
  args << {:type => 'D'} if !opts
205
- return self.puts(*args)
206
+ newline = (opts && opts.key?(:newline)) ? opts[:newline] : true
207
+ return newline ? self.puts(*args) : self.print(*args)
206
208
  end
207
209
  return true
208
210
  }
@@ -19,12 +19,18 @@
19
19
  #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  #SOFTWARE.
22
+
22
23
  require 'ostruct'
24
+ require_relative 'log'
25
+ require_relative 'sys'
26
+ require_relative 'module'
27
+
28
+ # Collection of network related helpers
29
+ module Net
30
+ extend self
31
+ mattr_accessor(:agents)
23
32
 
24
- # Collection of simply network related helpers
25
- module Net
26
- @@_proxy = nil
27
- @@_agents = OpenStruct.new({
33
+ @@agents = OpenStruct.new({
28
34
  windows_ie_6: 'Windows IE 6',
29
35
  windows_ie_7: 'Windows IE 7',
30
36
  windows_mozilla: 'Windows Mozilla',
@@ -37,40 +43,195 @@ module Net
37
43
  iphone: 'iPhone'
38
44
  })
39
45
 
40
- # Accessors
41
- def self.agents; @@_agents; end
42
- def self.proxy_uri; http_proxy ? http_proxy.split(':')[1][2..-1] : nil; end
43
- def self.proxy_port; http_proxy ? http_proxy.split(':').last : nil; end
44
- def self.ftp_proxy; get_proxy if @@_proxy.nil?; @@_proxy['ftp_proxy']; end
45
- def self.http_proxy; get_proxy if @@_proxy.nil?; @@_proxy['http_proxy']; end
46
- def self.https_proxy; get_proxy if @@_proxy.nil?; @@_proxy['https_proxy']; end
47
- def self.no_proxy; get_proxy if @@_proxy.nil?; @@_proxy['no_proxy']; end
48
-
49
- # Get the system proxy variables
50
- def self.get_proxy
51
- @@_proxy = {
52
- 'ftp_proxy' => ENV['ftp_proxy'],
53
- 'http_proxy' => ENV['http_proxy'],
54
- 'https_proxy' => ENV['https_proxy'],
55
- 'no_proxy' => ENV['no_proxy']
56
- }
46
+ # Get fresh proxy from environment
47
+ def proxy
48
+ return OpenStruct.new({
49
+ ftp: ENV['ftp_proxy'],
50
+ http: ENV['http_proxy'],
51
+ https: ENV['https_proxy'],
52
+ no: ENV['no_proxy'],
53
+ uri: ENV['http_proxy'] ? ENV['http_proxy'].split(':')[0..-2] * ":" : nil,
54
+ port: ENV['http_proxy'] ? ENV['http_proxy'].split(':').last : nil
55
+ })
57
56
  end
58
57
 
59
- # Get a shell export string for proxies
60
- def self.proxy_export
61
- get_proxy if @@_proxy.nil?
62
- return proxy_exist? ? (@@_proxy.map{|k,v| "export #{k}=#{v}"} * ';') + ";" : nil
58
+ # Check if a proxy is set
59
+ def proxy?
60
+ return !self.proxy.http.nil?
63
61
  end
64
62
 
65
- # Check if a proxy is set
66
- def self.proxy_exist?
67
- get_proxy if @@_proxy.nil?
68
- return !@@_proxy['http_proxy'].nil?
63
+ # Get a shell export string for proxies
64
+ # @param proxy [String] to use rather than default
65
+ def proxy_export(proxy:nil)
66
+ if proxy
67
+ ({'ftp_proxy' => proxy,
68
+ 'http_proxy' => proxy,
69
+ 'https_proxy' => proxy
70
+ }.map{|k,v| "export #{k}=#{v}"} * ';') + ";"
71
+ elsif self.proxy?
72
+ (self.proxy.to_h.map{|k,v| (![:uri, :port].include?(k) && v) ? "export #{k}_proxy=#{v}" : nil}.compact * ';') + ";"
73
+ else
74
+ return nil
75
+ end
69
76
  end
70
77
 
71
78
  # Check if the system is configured for the kernel to forward ip traffic
72
- def self.ip_forward?
73
- return `cat /proc/sys/net/ipv4/ip_forward`.include?('1')
79
+ def ip_forward?
80
+ return File.read('/proc/sys/net/ipv4/ip_forward').include?('1')
81
+ end
82
+
83
+ # ----------------------------------------------------------------------------
84
+ # Namespace related helpers
85
+ # ----------------------------------------------------------------------------
86
+
87
+ # Virtual Ethernet NIC object
88
+ # @param name [String] of the veth
89
+ # @param ip [String] of the veth
90
+ # @param nic [String] pattern matching nic e.g. en+
91
+ Veth = Struct.new(:name, :ip, :nic)
92
+
93
+ # Network object
94
+ # @param ip [String] of the network
95
+ # @param cidr [String] of the network
96
+ # @param nameservers [Array[String]] to use for new network
97
+ Network = Struct.new(:ip, :cidr, :nameservers)
98
+
99
+ # Check that the namespace has connectivity to the outside world
100
+ # using a simple curl on google
101
+ # @param namespace [String] name to use when creating it
102
+ # @param proxy [String] to use rather than default
103
+ def namespace_connectivity?(namespace, proxy:nil)
104
+ success = false
105
+ Log.info("Checking namespace #{namespace.colorize(:cyan)} for connectivity to google.com", newline:false)
106
+
107
+ if File.exists?(File.join("/var/run/netns", namespace))
108
+ ping = 'curl -sL -w "%{http_code}" http://www.google.com -o /dev/null'
109
+ return Sys.exec_status("ip netns exec #{namespace} bash -c '#{self.proxy_export(proxy)}#{ping}'", die:false, check:"200")
110
+ else
111
+ Sys.exec_status(":", die:false, check:"200")
112
+ Log.warn("Namespace #{namespace} doesn't exist!")
113
+ end
114
+ end
115
+
116
+ # Create a network namespace with the given name
117
+ # @param namespace [String] name to use when creating it
118
+ # @param host_veth [Veth] describes the veth to create for the host side
119
+ # @param guest_veth [Veth] describes the veth to create for the guest side
120
+ # @param network [Network] describes the network to share
121
+ def create_namespace(namespace, host_veth, guest_veth, network)
122
+ namespace_conf = File.join("/etc/netns", namespace)
123
+
124
+ # Ensure namespace i.e. /var/run/netns/<namespace> exists
125
+ if !File.exists?(File.join("/var/run/netns", namespace))
126
+ Log.info("Creating VPN Namespace #{namespace.colorize(:cyan)}", newline:false)
127
+ Sys.exec_status("ip netns add #{namespace}")
128
+ end
129
+
130
+ # Ensure loopback device is running inside the pnamespace
131
+ if `ip netns exec #{namespace} ip a`.include?("state DOWN")
132
+ Log.info("Start loopback interface in namespace", newline:false)
133
+ Sys.exec_status("ip netns exec #{namespace} ip link set lo up")
134
+ end
135
+
136
+ # Create a virtual ethernet pair to communicate across namespaces
137
+ # by default they will both be in the root namespace until one is assigned to another
138
+ # e.g. host:192.168.100.1 and guest:192.168.100.2 communicating in network:192.168.100.0
139
+ if !`ip a`.include?(host_veth.name)
140
+ Log.info("Create vpn veths #{host_veth.name.colorize(:cyan)} for #{'root'.colorize(:cyan)}
141
+ and #{guest_veth.name.colorize(:cyan)} for #{namespace.colorize(:cyan)}", newline:false)
142
+ Sys.exec_status("ip link add #{host_veth.name} type veth peer name #{guest_veth.name}")
143
+ Log.info("Assign veth #{guest_veth.name.colorize(:cyan)} to namespace #{namespace.colorize(:cyan)}", newline:false)
144
+ Sys.exec_status("ip link set #{guest_veth.name} netns #{namespace}")
145
+ end
146
+
147
+ # Assign IPv4 addresses and start up the new veth interfaces
148
+ # sudo ping #{host_veth.ip} and sudo netns exec #{namespace} ping #{guest_veth.ip} should work now
149
+ if !`ip a`.include?(host_veth.ip)
150
+ Log.info("Assign ip #{host_veth.ip.colorize(:cyan)} and start #{host_veth.ip.colorize(:cyan)}", newline:false)
151
+ Sys.exec_status("ifconfig #{host_veth.name} #{File.join(host_veth.ip, nework.cidr)} up")
152
+ end
153
+ if !`ip netns exec #{namespace} ip a`.include?(guest_veth.ip)
154
+ Log.info("Assign ip #{guest_veth.ip.colorize(:cyan)} and start #{guest_veth.ip.colorize(:cyan)}", newline:false)
155
+ Sys.exec_status("ip netns exec #{namespace} ifconfig #{guest_veth.name} #{File.join(guest_veth.ip, nework.cidr)} up")
156
+ end
157
+
158
+ # Share internet access on host with namespace
159
+ # Note: to see current forward rules use: iptables -S
160
+ if !`ip netns exec #{namespace} ip route`.include?('default')
161
+ Log.info("Set default route for traffic leaving namespace to #{host_veth.ip.colorize(:cyan)}", newline:false)
162
+ Sys.exec_status("ip netns exec #{namespace} ip route add default via #{host_veth.ip} dev #{guest_veth.name}")
163
+ end
164
+ if !`iptables -t nat -S`.include?(File.join(nework.ip, nework.cidr))
165
+ Log.info("Enable NAT on host for vpn net #{File.join(nework.ip, nework.cidr).colorize(:cyan)}", newline:false)
166
+ Sys.exec_status("iptables -t nat -A POSTROUTING -s #{File.join(nework.ip, nework.cidr)} -o #{host_veth.nic} -j MASQUERADE")
167
+ end
168
+ if !`iptables -S`.include?("-A FORWARD -i #{host_veth.nic}")
169
+ Log.info("Allow forwarding to #{namespace.colorize(:cyan)}", newline:false)
170
+ Sys.exec_status("iptables -A FORWARD -i #{host_veth.nic} -o #{host_veth.name} -j ACCEPT")
171
+ end
172
+ if !`iptables -S`.include?("-A FORWARD -i #{host_veth.name}")
173
+ Log.info("Allow forwarding from #{namespace.colorize(:cyan)}", newline:false)
174
+ Sys.exec_status("iptables -A FORWARD -i #{host_veth.name} -o #{host_veth.nic} -j ACCEPT")
175
+ end
176
+
177
+ # Configure nameserver to use in VPN namespace
178
+ namespace_conf = File.join("/etc/netns", namespace)
179
+ if !File.exists?(namespace_conf) && network.nameservers
180
+ Log.info("Creating nameserver config #{namespace_conf}", newline:false)
181
+ Sys.exec_status("mkdir -p #{namespace_conf}")
182
+ network.nameservers.each{|x|
183
+ Log.info("Adding nameserver #{x.colorize(:cyan)} to config", newline:false)
184
+ Sys.exec_status("echo 'nameserver #{x}' >> /etc/netns/#{namespace}/resolv.conf")
185
+ }
186
+ end
187
+ end
188
+
189
+ # Delete the given network namespace
190
+ # @param namespace [String] name to use when creating it
191
+ # @param host_veth [Veth] describes the veth to create for the host side
192
+ # @param network [Network] describes the network to share
193
+ def delete_namespace(namespace, host_veth, network)
194
+
195
+ # Remove nameserver config for vpn
196
+ namespace_conf = File.join("/etc/netns", namespace)
197
+ if File.exists?(namespace_conf)
198
+ Log.info("Removing nameserver config #{namespace_conf}", newline:false)
199
+ Sys.exec_status("rm -rf #{namespace_conf}")
200
+ end
201
+
202
+ # Remove NAT and iptables forwarding allowances
203
+ if `iptables -t nat -S`.include?(File.join(network.ip, network.cidr))
204
+ Log.info("Removing NAT on host for vpn net #{File.join(network.ip, network.cidr).colorize(:cyan)}", newline:false)
205
+ Sys.exec_status("iptables -t nat -D POSTROUTING -s #{File.join(network.ip, network.cidr)} -o #{host_veth.nic} -j MASQUERADE")
206
+ end
207
+ if `iptables -S`.include?("-A FORWARD -i #{host_veth.nic}")
208
+ Log.info("Allow forwarding to #{namespace.colorize(:cyan)}", newline:false)
209
+ Sys.exec_status("iptables -D FORWARD -i #{host_veth.nic} -o #{host_veth.name} -j ACCEPT")
210
+ end
211
+ if `iptables -S`.include?("-A FORWARD -i #{host_veth.name}")
212
+ Log.info("Allow forwarding from #{namespace.colorize(:cyan)}", newline:false)
213
+ Sys.exec_status("iptables -D FORWARD -i #{host_veth.name} -o #{host_veth.nic} -j ACCEPT")
214
+ end
215
+
216
+ # Remove virtual ethernet interfaces
217
+ if `ip a`.include?(host_veth.name)
218
+ Log.info("Removing veth interface #{host_veth.name.colorize(:cyan)} for vpn", newline:false)
219
+ Sys.exec_status("ip link delete #{host_veth.name}")
220
+ end
221
+
222
+ # Remove namespace for vpn
223
+ if File.exists?(File.join("/var/run/netns", namespace))
224
+ Log.info("Removing namespace #{namespace.colorize(:cyan)}", newline:false)
225
+ Sys.exec_status("ip netns delete #{namespace}")
226
+ end
227
+ end
228
+
229
+ # Execute in the namespace
230
+ # @param namespace [String] to execut within
231
+ # @param cmd [String] command to execute
232
+ # @param proxy [String] to use rather than default
233
+ def namespace_exec(namespace, cmd, proxy:nil)
234
+ return `ip netns exec #{namespace} bash -c '#{self.proxy_export(proxy)}#{cmd}'`
74
235
  end
75
236
  end
76
237
 
@@ -98,6 +98,27 @@ module Sys
98
98
  return result
99
99
  end
100
100
 
101
+ # Execute the shell command and print status
102
+ # @param cmd [String] command to execute
103
+ # @param die [bool] exit on true
104
+ # @result status [bool] true on success else false
105
+ def exec_status(cmd, die:true, check:nil)
106
+ out = `#{cmd}`
107
+ status = true
108
+ status = check == out if !check.nil?
109
+ status = $?.exitstatus == 0 if check.nil?
110
+
111
+ #if status
112
+ if status
113
+ Log.puts("...success!".colorize(:green), stamp:false)
114
+ else
115
+ Log.puts("...failed!".colorize(:red), stamp:false)
116
+ Log.puts(out.colorize(:red)) and exit if die
117
+ end
118
+
119
+ return status
120
+ end
121
+
101
122
  # Read a password from stdin without echoing
102
123
  # @returns pass [String] the password read in
103
124
  def getpass
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nub
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.96
4
+ version: 0.0.103
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Crummett
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-06-23 00:00:00.000000000 Z
11
+ date: 2018-08-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize