nub 0.0.125 → 0.0.128

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nub/config.rb +1 -1
  3. metadata +2 -3
  4. data/lib/nub/:w +0 -374
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe85adf0021df868b46e1b8f36f38d6b9f3deb42edc9721629bf6a2b915b3701
4
- data.tar.gz: 5988cdcaca7111a76b345f8c434f963a9730a37aac57dc9bb2a3e3818d381ff5
3
+ metadata.gz: bc495fbfdb92d3b2cbd5f8b34373e4ffd7ccfc5f9ddfede0140d1e52c8a2e89a
4
+ data.tar.gz: 27067ccd9364da31e08077f0cedce3fcf46594c6f2522a115695e5d310e908bf
5
5
  SHA512:
6
- metadata.gz: 11ff5cd8004bca05799bd22dff24780fb06174fbe30f2292c550480b485da23125d92eedea4d8719094e19abbbb912d914dbfcd972b105758a133e4dded008aa
7
- data.tar.gz: 0995e0bc2ba7f9e02e391e3cea2cc2a8771396934b3b82ad1aafa34b375acc8fb5e384787e28fa32c1165af51e4ab08c6a687dda969152dcf693a54231b2d5a5
6
+ metadata.gz: c16bb3b7b76f1d2c80a3bcc05044eb81105fa1014e33c15c82bfb1930b739cede6a40dd27606ee56bfcb846efdbb805bc574bdd4395f867994fcb21ccd9079ec
7
+ data.tar.gz: fb633b15decdf82c281d235a8e0fe1c1db04c30175cbef81a5680522d528cd87329b821525bced89e25aecc75681b6adace6438282038c5a511a4955d7a73158
data/lib/nub/config.rb CHANGED
@@ -29,7 +29,7 @@ require_relative 'log'
29
29
  # Uses singleton pattern for single source of truth
30
30
  module Config
31
31
  extend self
32
- @@_yml = nil
32
+ @@_yml = {}
33
33
 
34
34
  # Public properties
35
35
  class << self
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.125
4
+ version: 0.0.128
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-08-23 00:00:00.000000000 Z
11
+ date: 2018-08-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -89,7 +89,6 @@ files:
89
89
  - LICENSE
90
90
  - README.md
91
91
  - lib/nub.rb
92
- - lib/nub/:w
93
92
  - lib/nub/commander.rb
94
93
  - lib/nub/config.rb
95
94
  - lib/nub/core.rb
data/lib/nub/:w DELETED
@@ -1,374 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #MIT License
3
- #Copyright (c) 2018 phR0ze
4
- #
5
- #Permission is hereby granted, free of charge, to any person obtaining a copy
6
- #of this software and associated documentation files (the "Software"), to deal
7
- #in the Software without restriction, including without limitation the rights
8
- #to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- #copies of the Software, and to permit persons to whom the Software is
10
- #furnished to do so, subject to the following conditions:
11
- #
12
- #The above copyright notice and this permission notice shall be included in all
13
- #copies or substantial portions of the Software.
14
- #
15
- #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- #IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- #FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- #AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- #LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- #OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- #SOFTWARE.
22
-
23
- require 'ostruct'
24
- require 'ipaddr'
25
- require_relative 'log'
26
- require_relative 'sys'
27
- require_relative 'module'
28
-
29
- # Collection of network related helpers
30
- module Net
31
- extend self
32
- mattr_accessor(:agents)
33
- mattr_accessor(:namespace_subnet, :namespce_cidr)
34
-
35
- @@namespace_cidr = "24"
36
- @@namespace_subnet = "192.168.100.0"
37
-
38
- @@agents = OpenStruct.new({
39
- windows_ie_6: 'Windows IE 6',
40
- windows_ie_7: 'Windows IE 7',
41
- windows_mozilla: 'Windows Mozilla',
42
- mac_safari: 'Mac Safari',
43
- mac_firefox: 'Mac FireFox',
44
- mac_mozilla: 'Mac Mozilla',
45
- linux_mozilla: 'Linux Mozilla',
46
- linux_firefox: 'Linux Firefox',
47
- linux_konqueror: 'Linux Konqueror',
48
- iphone: 'iPhone'
49
- })
50
-
51
- # Get fresh proxy from environment
52
- def proxy
53
- return OpenStruct.new({
54
- ftp: ENV['ftp_proxy'],
55
- http: ENV['http_proxy'],
56
- https: ENV['https_proxy'],
57
- no: ENV['no_proxy'],
58
- uri: ENV['http_proxy'] ? ENV['http_proxy'].split(':')[0..-2] * ":" : nil,
59
- port: ENV['http_proxy'] ? ENV['http_proxy'].split(':').last : nil
60
- })
61
- end
62
-
63
- # Check if a proxy is set
64
- def proxy?
65
- return !self.proxy.http.nil?
66
- end
67
-
68
- # Get a shell export string for proxies
69
- # @param proxy [String] to use rather than default
70
- def proxy_export(*args)
71
- proxy = args.any? ? args.first.to_s : nil
72
- if proxy
73
- ({'ftp_proxy' => proxy,
74
- 'http_proxy' => proxy,
75
- 'https_proxy' => proxy
76
- }.map{|k,v| "export #{k}=#{v}"} * ';') + ";"
77
- elsif self.proxy?
78
- (self.proxy.to_h.map{|k,v| (![:uri, :port].include?(k) && v) ? "export #{k}_proxy=#{v}" : nil}.compact * ';') + ";"
79
- else
80
- return nil
81
- end
82
- end
83
-
84
- # Check if the system is configured for the kernel to forward ip traffic
85
- def ip_forward?
86
- return File.read('/proc/sys/net/ipv4/ip_forward').include?('1')
87
- end
88
-
89
- # Determine the primary nic on the machine
90
- # based off default routing to google.com
91
- # @returns [String] nic identified as primary
92
- def primary_nic
93
- out = `ip route`
94
- return out[/default via.*dev (.*) proto/, 1]
95
- end
96
-
97
- # Increment the given ip address
98
- # @param ip [String] ip address to increment
99
- # @param i [int] optionally increment by given number
100
- # @returns [String] incremented ip
101
- def ipinc(ip, *args)
102
- i = args.any? ? args.first.to_i : 1
103
- ip_i = IPAddr.new(ip).to_i + i
104
- return [24, 16, 8, 0].collect{|x| (ip_i >> x) & 255}.join('.')
105
- end
106
-
107
- # Decrement the given ip address
108
- # @param ip [String] ip address to decrement
109
- # @param i [int] optionally decrement by given number
110
- # @returns [String] decremented ip
111
- def ipdec(ip, *args)
112
- i = args.any? ? args.first.to_i : 1
113
- ip_i = IPAddr.new(ip).to_i - i
114
- return [24, 16, 8, 0].collect{|x| (ip_i >> x) & 255}.join('.')
115
- end
116
-
117
- # ----------------------------------------------------------------------------
118
- # Namespace related helpers
119
- # ----------------------------------------------------------------------------
120
-
121
- # Virtual Ethernet NIC object
122
- # @param name [String] of the veth e.g. veth1
123
- # @param ip [String] of the veth e.g. 192.168.100.1
124
- Veth = Struct.new(:name, :ip)
125
-
126
- # Network object
127
- # @param subnet [String] of the network e.g. 192.168.100.0
128
- # @param cidr [String] of the network
129
- # @param nic [String] to use for NAT etc...
130
- # @param nameservers [Array[String]] to optionally use for new network else uses hosts
131
- Network = Struct.new(:subnet, :cidr, :nic, :nameservers)
132
-
133
- # Get all namespaces
134
- # @returns [Array] of namespace names
135
- def namespaces
136
- return Dir[File.join("/var/run/netns", "*")].map{|x| File.basename(x)}
137
- end
138
-
139
- # Get the current nameservers in use
140
- # @param filename [String] to use instead of /etc/resolv.conf
141
- # @returns [Array] of name server ips
142
- def nameservers(*args)
143
- filename = args.any? ? args.first.to_s : '/etc/resolv.conf'
144
-
145
- result = []
146
- if File.file?(filename)
147
- File.readlines(filename).each{|line|
148
- if line[/nameserver/]
149
- result << line[/nameserver\s+(.*)/, 1]
150
- end
151
- }
152
- end
153
- return result
154
- end
155
-
156
- # Check that the namespace has connectivity to the outside world
157
- # using a simple curl on google
158
- # @param namespace [String] name to use when creating it
159
- # @param target [String] ip or dns name to use for check
160
- # @param proxy [String] to use rather than default
161
- def namespace_connectivity?(namespace, target, *args)
162
- success = false
163
- proxy = args.any? ? args.first.to_s : nil
164
- Log.info("Checking namespace #{namespace.colorize(:cyan)} for connectivity to #{target}", newline:false)
165
-
166
- if self.namespaces.include?(namespace)
167
- ping = "curl -m 3 -sL -w \"%{http_code}\" #{target} -o /dev/null"
168
- return Sys.exec_status("ip netns exec #{namespace} bash -c '#{self.proxy_export(proxy)}#{ping}'", die:false, check:"200")
169
- else
170
- Sys.exec_status(":", die:false, check:"200")
171
- Log.warn("Namespace #{namespace} doesn't exist!")
172
- end
173
- end
174
-
175
- # Get namespace details using defaults for missing arguments
176
- # veth names are generated incrementally using the '<ns>_vethN' naming pattern
177
- # veth ips are generated based off @@namespace_subnet/@@namespace_cidr incrementally
178
- # network subnet and cidr default and namespaces and nic are looked up
179
- # @param namespace [String] name to use when creating it
180
- # @returns [host_veth, guest_veth, network]
181
- def namespace_details(namespace, *args)
182
- host_veth, guest_veth = Veth.new, Veth.new
183
- network = Network.new(@@namespace_subnet, @@namespace_cidr, true)
184
-
185
- # Rebuild namespace objects from disk where possible
186
- if self.namespaces.include?(namespace)
187
-
188
- # Rebuild veths from guest
189
- out = `ip netns exec #{namespace} ip a show type veth`
190
- if hostif = out[/([\d]+):\s+.*@if[\d]+/, 1] ? "if" + out[/([\d]+):\s+.*@if[\d]+/, 1] : nil
191
- guest_veth.name = out[/ (.*)@if[\d]+/, 1]
192
- guest_veth.ip = out[/inet\s+([\d]+\.[\d]+\.[\d]+\.[\d]+).*/, 1]
193
- host_veth.name = out[/ (.*)@#{hostif}/, 1]
194
- host_veth.ip = self.ipdec(guest_veth.ip)
195
-
196
- # Rebuild veths from host
197
- else
198
- # out = `ip a show type veth`
199
- # host_str = out[/inet(.*)#{host_veth.name}/, 1]
200
- # host_ip = host_str[/\s*([\d]+\.[\d]+\.[\d]+\.[\d]+\/[\d]+).*/, 1] if host_str
201
- # host_veth.ip = host_ip[/(.*)\/[\d]+/, 1] if host_ip
202
- end
203
-
204
- # Rebuild network object
205
- namespace_conf = File.join("/etc/netns", namespace, "resolv.conf")
206
- network.nameservers = self.nameservers(namespace_conf) if File.exists?(namespace_conf)
207
- network.cidr = host_ip[/\/([\d]+)/, 1] if host_ip
208
- network.subnet = IPAddr.new(host_veth.ip).mask(network.cidr).to_s
209
- out = `iptables -S`
210
- if out.include?(host_veth.name)
211
- network.nic = out[/-A FORWARD -i #{host_veth.name} -o (.*) -j ACCEPT/, 1]
212
- end
213
-
214
- # Handle args as either as positional or named
215
- else
216
- if args.size == 1 && args.first.is_a?(Hash)
217
- network = args.first[:network] if args.first.key?(:network)
218
- host_veth = args.first[:host_veth] if args.first.key?(:host_veth)
219
- guest_veth = args.first[:guest_veth] if args.first.key?(:guest_veth)
220
- elsif args.size > 1
221
- host_veth = args.shift
222
- guest_veth = args.shift if args.any?
223
- network = args.shift if args.any?
224
- end
225
- end
226
-
227
- # Populate missing information
228
- nss = self.namespaces
229
- i = (nss.include?(namespace) ? nss.size - 1 : nss.size) * 2 + 1
230
- host_veth.name = "#{namespace}_veth#{i}" if !host_veth.name
231
- host_veth.ip = self.ipinc(@@namespace_subnet, i) if !host_veth.ip
232
- guest_veth.name = "#{namespace}_veth#{i + 1}" if !guest_veth.name
233
- guest_veth.ip = "#{self.ipinc(host_veth.ip)}" if !guest_veth.ip
234
- network.nic = self.primary_nic if network.nic == true
235
- network.nameservers = self.nameservers if not network.nameservers
236
-
237
- return host_veth, guest_veth, network
238
- end
239
-
240
- # Create a network namespace with the given name
241
- # Params can be given as ordered positional args or named args
242
- #
243
- # @param namespace [String] name to use when creating it
244
- # @param host_veth [Veth] describes the veth to create for the host side
245
- # @param guest_veth [Veth] describes the veth to create for the guest side
246
- # @param network [Network] describes the network to share.
247
- # If the nic param is nil NAT is not enabled. If nic is true then the primary nic is dynamically
248
- # looked up else use user given If nameservers are not given the host nameservers will be used
249
- #def create_namespace(namespace, host_veth, guest_veth, network)
250
- def create_namespace(namespace, *args)
251
- host_veth, guest_veth, network = self.namespace_details(namespace, args)
252
-
253
- # Ensure namespace i.e. /var/run/netns/<namespace> exists
254
- if !self.namespaces.include?(namespace)
255
- Log.info("Creating Network Namespace #{namespace.colorize(:cyan)}", newline:false)
256
- Sys.exec_status("ip netns add #{namespace}")
257
- end
258
-
259
- # Ensure loopback device is running inside the pnamespace
260
- if `ip netns exec #{namespace} ip a`.include?("state DOWN")
261
- Log.info("Start loopback interface in namespace", newline:false)
262
- Sys.exec_status("ip netns exec #{namespace} ip link set lo up")
263
- end
264
-
265
- # Create a virtual ethernet pair to communicate across namespaces
266
- # by default they will both be in the root namespace until one is assigned to another
267
- # e.g. host:192.168.100.1 and guest:192.168.100.2 communicating in network:192.168.100.0
268
- if !`ip a`.include?(host_veth.name)
269
- msg = "Create namespace veths #{host_veth.name.colorize(:cyan)} for #{'root'.colorize(:cyan)} "
270
- msg += "and #{guest_veth.name.colorize(:cyan)} for #{namespace.colorize(:cyan)}"
271
- Log.info(msg, newline:false)
272
- Sys.exec_status("ip link add #{host_veth.name} type veth peer name #{guest_veth.name}")
273
- Log.info("Assign veth #{guest_veth.name.colorize(:cyan)} to namespace #{namespace.colorize(:cyan)}", newline:false)
274
- Sys.exec_status("ip link set #{guest_veth.name} netns #{namespace}")
275
- end
276
-
277
- # Assign IPv4 addresses and start up the new veth interfaces
278
- # sudo ping #{host_veth.ip} and sudo netns exec #{namespace} ping #{guest_veth.ip} should work now
279
- if !`ip a`.include?(host_veth.ip)
280
- Log.info("Assign ip #{host_veth.ip.colorize(:cyan)} and start #{host_veth.name.colorize(:cyan)}", newline:false)
281
- Sys.exec_status("ifconfig #{host_veth.name} #{File.join(host_veth.ip, network.cidr)} up")
282
- end
283
- if !`ip netns exec #{namespace} ip a`.include?(guest_veth.ip)
284
- Log.info("Assign ip #{guest_veth.ip.colorize(:cyan)} and start #{guest_veth.name.colorize(:cyan)}", newline:false)
285
- Sys.exec_status("ip netns exec #{namespace} ifconfig #{guest_veth.name} #{File.join(guest_veth.ip, network.cidr)} up")
286
- end
287
-
288
- # Configure host veth as guest's default route leaving namespace
289
- if !`ip netns exec #{namespace} ip route`.include?('default')
290
- Log.info("Set default route for traffic leaving namespace #{namespace.colorize(:cyan)} to #{host_veth.ip.colorize(:cyan)}", newline:false)
291
- Sys.exec_status("ip netns exec #{namespace} ip route add default via #{host_veth.ip} dev #{guest_veth.name}")
292
- end
293
-
294
- # NAT guest veth behind host veth to share internet access on host with guest
295
- # Note: to see current forward rules use: sudo iptables -S
296
- if network.nic
297
- if !`iptables -t nat -S`.include?(File.join(network.subnet, network.cidr))
298
- Log.info("Enable NAT on host for namespace #{File.join(network.subnet, network.cidr).colorize(:cyan)}", newline:false)
299
- Sys.exec_status("iptables -t nat -A POSTROUTING -s #{File.join(network.subnet, network.cidr)} -o #{network.nic} -j MASQUERADE")
300
- end
301
- if !`iptables -S`.include?("-A FORWARD -i #{network.nic}")
302
- Log.info("Allow forwarding to #{namespace.colorize(:cyan)} from #{network.nic.colorize(:cyan)}", newline:false)
303
- Sys.exec_status("iptables -A FORWARD -i #{network.nic} -o #{host_veth.name} -j ACCEPT")
304
- end
305
- if !`iptables -S`.include?("-A FORWARD -i #{host_veth.name}")
306
- Log.info("Allow forwarding from #{namespace.colorize(:cyan)} to #{network.nic.colorize(:cyan)}", newline:false)
307
- Sys.exec_status("iptables -A FORWARD -i #{host_veth.name} -o #{network.nic} -j ACCEPT")
308
- end
309
- end
310
-
311
- # Configure nameserver to use in Network namespace
312
- namespace_conf = File.join("/etc/netns", namespace)
313
- if !File.exists?(namespace_conf) && network.nameservers
314
- Log.info("Creating nameserver config #{namespace_conf}", newline:false)
315
- Sys.exec_status("mkdir -p #{namespace_conf}")
316
- network.nameservers.each{|x|
317
- Log.info("Adding nameserver #{x.colorize(:cyan)} to config", newline:false)
318
- Sys.exec_status("echo 'nameserver #{x}' >> /etc/netns/#{namespace}/resolv.conf")
319
- }
320
- end
321
- end
322
-
323
- # Delete the given network namespace
324
- # @param namespace [String] name to use when creating it
325
- def delete_namespace(namespace)
326
- host_veth, guest_veth, network = self.namespace_details(namespace)
327
-
328
- # Remove nameserver config for network namespace
329
- namespace_conf = File.join("/etc/netns", namespace)
330
- if File.exists?(namespace_conf)
331
- Log.info("Removing nameserver config #{namespace_conf.colorize(:cyan)}", newline:false)
332
- Sys.exec_status("rm -rf #{namespace_conf}")
333
- end
334
-
335
- # Remove NAT and iptables forwarding allowances
336
- if network.nic
337
- if `iptables -t nat -S`.include?(File.join(network.subnet, network.cidr))
338
- Log.info("Removing NAT on host for namespace #{File.join(network.subnet, network.cidr).colorize(:cyan)}", newline:false)
339
- Sys.exec_status("iptables -t nat -D POSTROUTING -s #{File.join(network.subnet, network.cidr)} -o #{network.nic} -j MASQUERADE")
340
- end
341
- if `iptables -S`.include?("-A FORWARD -i #{network.nic}")
342
- Log.info("Remove forwarding to #{namespace.colorize(:cyan)} from #{host_veth.ip.colorize(:cyan)}", newline:false)
343
- Sys.exec_status("iptables -D FORWARD -i #{network.nic} -o #{host_veth.name} -j ACCEPT")
344
- end
345
- if `iptables -S`.include?("-A FORWARD -i #{host_veth.name}")
346
- Log.info("Remove forwarding from #{namespace.colorize(:cyan)} to #{host_veth.ip.colorize(:cyan)}", newline:false)
347
- Sys.exec_status("iptables -D FORWARD -i #{host_veth.name} -o #{network.nic} -j ACCEPT")
348
- end
349
- end
350
-
351
- # Remove veths (virtual ethernet interfaces)
352
- if `ip a`.include?(host_veth.name)
353
- Log.info("Removing veth interface #{host_veth.name.colorize(:cyan)} for namespace", newline:false)
354
- Sys.exec_status("ip link delete #{host_veth.name}")
355
- end
356
-
357
- # Remove namespace
358
- if self.namespaces.include?(namespace)
359
- Log.info("Removing namespace #{namespace.colorize(:cyan)}", newline:false)
360
- Sys.exec_status("ip netns delete #{namespace}")
361
- end
362
- end
363
-
364
- # Execute in the namespace
365
- # @param namespace [String] to execut within
366
- # @param cmd [String] command to execute
367
- # @param proxy [String] to use rather than default
368
- def namespace_exec(namespace, cmd, *args)
369
- proxy = args.any? ? args.first.to_s : nil
370
- return `ip netns exec #{namespace} bash -c '#{self.proxy_export(proxy)}#{cmd}'`
371
- end
372
- end
373
-
374
- # vim: ft=ruby:ts=2:sw=2:sts=2