nub 0.0.125 → 0.0.128

Sign up to get free protection for your applications and to get access to all the features.
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