nub 0.0.103 → 0.0.119
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.
- checksums.yaml +4 -4
- data/README.md +65 -14
- data/lib/nub/:w +374 -0
- data/lib/nub/net.rb +170 -59
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d7c15f5f66c73221685a124f105c3cca8205d41155603111823e8d580783aea7
|
4
|
+
data.tar.gz: 5d1bbe7937d9befa92a78237d6554993490e3e492b2dd75c5721e4457c2e630e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e2d6a2b5344de24c51ddb4913dd1225de0da8e6aeead3a527138bf124bfbe96a309f50ce5cd4b5b572a33bcdaf048da9cde1197c7a4dfb76f9a037e80b740d9
|
7
|
+
data.tar.gz: 11bb6954b62eb0326ac9015cd517808edaaabfeed0ec3d220448a80bd91b5ad84a03c8a94deed1c63f29d00a80eae5e3b1fe87fcc43290d902566de35393f0c9
|
data/README.md
CHANGED
@@ -25,6 +25,7 @@ Collection of ruby utils I've used in several of my projects and wanted re-usabl
|
|
25
25
|
* [Examples](#examples)
|
26
26
|
* [Indicators](#indicators)
|
27
27
|
* [Config Module](#config-module)
|
28
|
+
* [ERB Resolution](#erb-resolution)
|
28
29
|
* [Core Module](#core-module)
|
29
30
|
* [FileUtils Extensions](#fileutils-extensions)
|
30
31
|
* [Hash Module](#hash-module)
|
@@ -350,6 +351,18 @@ Config.init("openvpn.yml")
|
|
350
351
|
```
|
351
352
|
|
352
353
|
## Core Module <a name="core-module"></a>
|
354
|
+
The core module provides a few extensions to common ruby types.
|
355
|
+
|
356
|
+
### ERB Resolution <a name="erb-resolution"></a>
|
357
|
+
The ***String*** class has been extended to include ***.erb*** and ***.erb!*** to easily resolve
|
358
|
+
template variables. Additionally the ***Array*** and ***Hash*** classes have been extended to
|
359
|
+
recursively resolve template variables with ***.erb*** and ***.erb!*** functions.
|
360
|
+
|
361
|
+
Examples:
|
362
|
+
```ruby
|
363
|
+
puts("This is a template example <%=foo%>.".erb({'foo': 'foobar'}))
|
364
|
+
# outputs: This is a template example foobar.
|
365
|
+
```
|
353
366
|
|
354
367
|
## FileUtils Extensions <a name="fileutils-module"></a>
|
355
368
|
|
@@ -367,11 +380,11 @@ encapsulate functionality into reusable components.
|
|
367
380
|
Linux by default shares a single set of network interfaces and routing table entries, such that an
|
368
381
|
installed application can bind to all interfaces and has access to all other services currently
|
369
382
|
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
|
371
|
-
apps from the host and other docker apps.
|
383
|
+
networks, interfaces and services from each other. This is the technology that docker uses to
|
384
|
+
isolate networking in docker apps from the host and other docker apps.
|
372
385
|
|
373
|
-
Network namespaces provide a way to have
|
374
|
-
|
386
|
+
Network namespaces provide a way to have separate virtual interfaces and routing tables that operate
|
387
|
+
independent of each other. They can be manipulated via the `ip netns` command.
|
375
388
|
|
376
389
|
```bash
|
377
390
|
# Create a new network namespace
|
@@ -382,8 +395,8 @@ sudo ip netns add foo
|
|
382
395
|
ip netns list
|
383
396
|
```
|
384
397
|
|
385
|
-
Once a network namespace has been created you need to configure how it
|
386
|
-
can be done by creating a pair of virtual Ethernet ***veth*** interfaces and assigning them to the
|
398
|
+
Once a network namespace has been created you need to configure how/if it connects to the host. This
|
399
|
+
can be done by creating a pair of virtual Ethernet (***veth***) interfaces and assigning them to the
|
387
400
|
new network namespace as by default they will be assigned to the root namespace. The root namespace
|
388
401
|
or global namespace is the default namespace used by the host for regular networking.
|
389
402
|
|
@@ -429,7 +442,7 @@ sudo ip netns exec foo ping 192.168.100.1
|
|
429
442
|
# 64 bytes from 192.168.100.1: icmp_seq=1 ttl=64 time=0.080 ms
|
430
443
|
```
|
431
444
|
|
432
|
-
Now we have connectivity between our veth pair across network namespaces. Because
|
445
|
+
Now we have connectivity between our veth pair across network namespaces. Because the
|
433
446
|
***192.168.100.0/24*** network, that we configured the veth pair on, is a separate network from the
|
434
447
|
host any applications running within the new network namespace will not have connectivity to
|
435
448
|
anything else on your host or networks currently including external access to the internet.
|
@@ -443,18 +456,38 @@ sudo ip netns exec foo ping www.google.com
|
|
443
456
|
# connect: Network is unreachable
|
444
457
|
```
|
445
458
|
|
446
|
-
In the following sub sections I'll show you how to automated this
|
459
|
+
In the following sub sections I'll show you how to automated this complicated setup using the
|
447
460
|
***Net*** ruby module.
|
448
461
|
|
449
462
|
#### TeamViewer Example <a name="teamviewer-example"></a>
|
450
463
|
In this example I'll be showing you how to isolate Teamviewer such that Teamviewer is only able to
|
451
|
-
bind to the
|
452
|
-
This will allow you to have Teamviewer running and accessible from your network
|
453
|
-
to be able to SSH port forward other Teamviewer instances to your
|
454
|
-
addresses.
|
464
|
+
bind to the veth IPv4 address that we create for it rather than all network interfaces on the host.
|
465
|
+
This will allow you to have a local Teamviewer instance running and accessible from your network
|
466
|
+
facing IP but also to be able to SSH port forward other Teamviewer instances to your veth addresses.
|
455
467
|
|
456
|
-
```
|
457
|
-
|
468
|
+
```bash
|
469
|
+
require_relative '../lib/nub/net'
|
470
|
+
require_relative '../lib/nub/user'
|
471
|
+
!puts("Must be root to execute") and exit if not User.root?
|
472
|
+
|
473
|
+
if ARGV.size > 0
|
474
|
+
cmd = ARGV[0]
|
475
|
+
app = ARGV[1]
|
476
|
+
namespace = "foo"
|
477
|
+
host_veth = Net::Veth.new("veth1", "192.168.100.1")
|
478
|
+
guest_veth = Net::Veth.new("veth2", "192.168.100.2")
|
479
|
+
network = Net::Network.new("192.168.100.0", "24")
|
480
|
+
if cmd == "isolate"
|
481
|
+
Net.create_namespace(namespace, host_veth, guest_veth, network, "enp+")
|
482
|
+
Net.namespace_connectivity?(namespace, "google.com")
|
483
|
+
Net.namespace_exec(namespace, "lxterminal")
|
484
|
+
elsif cmd == "destroy"
|
485
|
+
Net.delete_namespace(namespace, host_veth, network, "enp+")
|
486
|
+
end
|
487
|
+
else
|
488
|
+
puts("Isolate: #{$0} isolate <app>")
|
489
|
+
puts("Destroy: #{$0} destroy")
|
490
|
+
end
|
458
491
|
```
|
459
492
|
|
460
493
|
#### PIA VPN Example <a name="pia-vpn-example"></a>
|
@@ -488,6 +521,11 @@ Net.proxy?
|
|
488
521
|
Net.proxy_export
|
489
522
|
```
|
490
523
|
|
524
|
+
```bash
|
525
|
+
# Ping type equivalent that works behind a proxy
|
526
|
+
curl -m 3 -sL -w "%{http_code}" google.com -o /dev/null
|
527
|
+
```
|
528
|
+
|
491
529
|
## Pacman Module <a name="pacman-module"></a>
|
492
530
|
|
493
531
|
## Process Module <a name="process-module"></a>
|
@@ -498,6 +536,19 @@ Net.proxy_export
|
|
498
536
|
|
499
537
|
## User Module <a name="user-module"></a>
|
500
538
|
|
539
|
+
```ruby
|
540
|
+
require 'nub'
|
541
|
+
|
542
|
+
# Check if the user is root
|
543
|
+
User.root?
|
544
|
+
|
545
|
+
# Drop root privileges to regular user that invoked sudo
|
546
|
+
User.drop_privileges
|
547
|
+
|
548
|
+
# Raise privileges back to sudo user if previously dropped
|
549
|
+
User.raise_privileges
|
550
|
+
```
|
551
|
+
|
501
552
|
## Ruby Gem Creation <a name="ruby-gem-creation"></a>
|
502
553
|
http://guides.rubygems.org/make-your-own-gem/
|
503
554
|
|
data/lib/nub/:w
ADDED
@@ -0,0 +1,374 @@
|
|
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
|
data/lib/nub/net.rb
CHANGED
@@ -21,6 +21,7 @@
|
|
21
21
|
#SOFTWARE.
|
22
22
|
|
23
23
|
require 'ostruct'
|
24
|
+
require 'ipaddr'
|
24
25
|
require_relative 'log'
|
25
26
|
require_relative 'sys'
|
26
27
|
require_relative 'module'
|
@@ -29,6 +30,10 @@ require_relative 'module'
|
|
29
30
|
module Net
|
30
31
|
extend self
|
31
32
|
mattr_accessor(:agents)
|
33
|
+
mattr_accessor(:namespace_subnet, :namespce_cidr)
|
34
|
+
|
35
|
+
@@namespace_cidr = "24"
|
36
|
+
@@namespace_subnet = "192.168.100.0"
|
32
37
|
|
33
38
|
@@agents = OpenStruct.new({
|
34
39
|
windows_ie_6: 'Windows IE 6',
|
@@ -62,7 +67,8 @@ module Net
|
|
62
67
|
|
63
68
|
# Get a shell export string for proxies
|
64
69
|
# @param proxy [String] to use rather than default
|
65
|
-
def proxy_export(
|
70
|
+
def proxy_export(*args)
|
71
|
+
proxy = args.any? ? args.first.to_s : nil
|
66
72
|
if proxy
|
67
73
|
({'ftp_proxy' => proxy,
|
68
74
|
'http_proxy' => proxy,
|
@@ -80,32 +86,85 @@ module Net
|
|
80
86
|
return File.read('/proc/sys/net/ipv4/ip_forward').include?('1')
|
81
87
|
end
|
82
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
|
+
|
83
117
|
# ----------------------------------------------------------------------------
|
84
118
|
# Namespace related helpers
|
85
119
|
# ----------------------------------------------------------------------------
|
86
120
|
|
87
121
|
# Virtual Ethernet NIC object
|
88
|
-
# @param name [String] of the veth
|
89
|
-
# @param ip [String] of the veth
|
90
|
-
|
91
|
-
Veth = Struct.new(:name, :ip, :nic)
|
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)
|
92
125
|
|
93
126
|
# Network object
|
94
|
-
# @param
|
127
|
+
# @param subnet [String] of the network e.g. 192.168.100.0
|
95
128
|
# @param cidr [String] of the network
|
96
|
-
# @param
|
97
|
-
|
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
|
98
155
|
|
99
156
|
# Check that the namespace has connectivity to the outside world
|
100
157
|
# using a simple curl on google
|
101
158
|
# @param namespace [String] name to use when creating it
|
159
|
+
# @param target [String] ip or dns name to use for check
|
102
160
|
# @param proxy [String] to use rather than default
|
103
|
-
def namespace_connectivity?(namespace,
|
161
|
+
def namespace_connectivity?(namespace, target, *args)
|
104
162
|
success = false
|
105
|
-
|
163
|
+
proxy = args.any? ? args.first.to_s : nil
|
164
|
+
Log.info("Checking namespace #{namespace.colorize(:cyan)} for connectivity to #{target}", newline:false)
|
106
165
|
|
107
|
-
if
|
108
|
-
ping =
|
166
|
+
if self.namespaces.include?(namespace)
|
167
|
+
ping = "curl -m 3 -sL -w \"%{http_code}\" #{target} -o /dev/null"
|
109
168
|
return Sys.exec_status("ip netns exec #{namespace} bash -c '#{self.proxy_export(proxy)}#{ping}'", die:false, check:"200")
|
110
169
|
else
|
111
170
|
Sys.exec_status(":", die:false, check:"200")
|
@@ -113,17 +172,55 @@ module Net
|
|
113
172
|
end
|
114
173
|
end
|
115
174
|
|
175
|
+
# Get namespace details using defaults for missing arguments
|
176
|
+
# veth names are generated using the '<ns>_<type>' 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
|
+
# Handle args as either as positional or named
|
186
|
+
if args.size == 1 && args.first.is_a?(Hash)
|
187
|
+
network = args.first[:network] if args.first.key?(:network)
|
188
|
+
host_veth = args.first[:host_veth] if args.first.key?(:host_veth)
|
189
|
+
guest_veth = args.first[:guest_veth] if args.first.key?(:guest_veth)
|
190
|
+
elsif args.size > 1
|
191
|
+
host_veth = args.shift
|
192
|
+
guest_veth = args.shift if args.any?
|
193
|
+
network = args.shift if args.any?
|
194
|
+
end
|
195
|
+
|
196
|
+
# Populate missing information
|
197
|
+
nss = self.namespaces
|
198
|
+
i = (nss.include?(namespace) ? nss.size - 1 : nss.size) * 2 + 1
|
199
|
+
host_veth.name = "#{namespace}_host" if !host_veth.name
|
200
|
+
host_veth.ip = self.ipinc(@@namespace_subnet, i) if !host_veth.ip
|
201
|
+
guest_veth.name = "#{namespace}_guest" if !guest_veth.name
|
202
|
+
guest_veth.ip = "#{self.ipinc(host_veth.ip)}" if !guest_veth.ip
|
203
|
+
network.nic = self.primary_nic if network.nic == true
|
204
|
+
network.nameservers = self.nameservers if not network.nameservers
|
205
|
+
|
206
|
+
return host_veth, guest_veth, network
|
207
|
+
end
|
208
|
+
|
116
209
|
# Create a network namespace with the given name
|
210
|
+
# Params can be given as ordered positional args or named args
|
211
|
+
#
|
117
212
|
# @param namespace [String] name to use when creating it
|
118
213
|
# @param host_veth [Veth] describes the veth to create for the host side
|
119
214
|
# @param guest_veth [Veth] describes the veth to create for the guest side
|
120
|
-
# @param network [Network] describes the network to share
|
121
|
-
|
122
|
-
|
215
|
+
# @param network [Network] describes the network to share.
|
216
|
+
# If the nic param is nil NAT is not enabled. If nic is true then the primary nic is dynamically
|
217
|
+
# looked up else use user given If nameservers are not given the host nameservers will be used
|
218
|
+
def create_namespace(namespace, *args)
|
219
|
+
host_veth, guest_veth, network = self.namespace_details(namespace, args)
|
123
220
|
|
124
221
|
# Ensure namespace i.e. /var/run/netns/<namespace> exists
|
125
|
-
if !
|
126
|
-
Log.info("Creating
|
222
|
+
if !self.namespaces.include?(namespace)
|
223
|
+
Log.info("Creating Network Namespace #{namespace.colorize(:cyan)}", newline:false)
|
127
224
|
Sys.exec_status("ip netns add #{namespace}")
|
128
225
|
end
|
129
226
|
|
@@ -137,8 +234,9 @@ module Net
|
|
137
234
|
# by default they will both be in the root namespace until one is assigned to another
|
138
235
|
# e.g. host:192.168.100.1 and guest:192.168.100.2 communicating in network:192.168.100.0
|
139
236
|
if !`ip a`.include?(host_veth.name)
|
140
|
-
|
141
|
-
|
237
|
+
msg = "Create namespace veths #{host_veth.name.colorize(:cyan)} for #{'root'.colorize(:cyan)} "
|
238
|
+
msg += "and #{guest_veth.name.colorize(:cyan)} for #{namespace.colorize(:cyan)}"
|
239
|
+
Log.info(msg, newline:false)
|
142
240
|
Sys.exec_status("ip link add #{host_veth.name} type veth peer name #{guest_veth.name}")
|
143
241
|
Log.info("Assign veth #{guest_veth.name.colorize(:cyan)} to namespace #{namespace.colorize(:cyan)}", newline:false)
|
144
242
|
Sys.exec_status("ip link set #{guest_veth.name} netns #{namespace}")
|
@@ -147,34 +245,38 @@ module Net
|
|
147
245
|
# Assign IPv4 addresses and start up the new veth interfaces
|
148
246
|
# sudo ping #{host_veth.ip} and sudo netns exec #{namespace} ping #{guest_veth.ip} should work now
|
149
247
|
if !`ip a`.include?(host_veth.ip)
|
150
|
-
Log.info("Assign ip #{host_veth.ip.colorize(:cyan)} and start #{host_veth.
|
151
|
-
Sys.exec_status("ifconfig #{host_veth.name} #{File.join(host_veth.ip,
|
248
|
+
Log.info("Assign ip #{host_veth.ip.colorize(:cyan)} and start #{host_veth.name.colorize(:cyan)}", newline:false)
|
249
|
+
Sys.exec_status("ifconfig #{host_veth.name} #{File.join(host_veth.ip, network.cidr)} up")
|
152
250
|
end
|
153
251
|
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.
|
155
|
-
Sys.exec_status("ip netns exec #{namespace} ifconfig #{guest_veth.name} #{File.join(guest_veth.ip,
|
252
|
+
Log.info("Assign ip #{guest_veth.ip.colorize(:cyan)} and start #{guest_veth.name.colorize(:cyan)}", newline:false)
|
253
|
+
Sys.exec_status("ip netns exec #{namespace} ifconfig #{guest_veth.name} #{File.join(guest_veth.ip, network.cidr)} up")
|
156
254
|
end
|
157
255
|
|
158
|
-
#
|
159
|
-
# Note: to see current forward rules use: iptables -S
|
256
|
+
# Configure host veth as guest's default route leaving namespace
|
160
257
|
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)
|
258
|
+
Log.info("Set default route for traffic leaving namespace #{namespace.colorize(:cyan)} to #{host_veth.ip.colorize(:cyan)}", newline:false)
|
162
259
|
Sys.exec_status("ip netns exec #{namespace} ip route add default via #{host_veth.ip} dev #{guest_veth.name}")
|
163
260
|
end
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
261
|
+
|
262
|
+
# NAT guest veth behind host veth to share internet access on host with guest
|
263
|
+
# Note: to see current forward rules use: sudo iptables -S
|
264
|
+
if network.nic
|
265
|
+
if !`iptables -t nat -S`.include?(File.join(network.subnet, network.cidr))
|
266
|
+
Log.info("Enable NAT on host for namespace #{File.join(network.subnet, network.cidr).colorize(:cyan)}", newline:false)
|
267
|
+
Sys.exec_status("iptables -t nat -A POSTROUTING -s #{File.join(network.subnet, network.cidr)} -o #{network.nic} -j MASQUERADE")
|
268
|
+
end
|
269
|
+
if !`iptables -S`.include?("-A FORWARD -i #{network.nic}")
|
270
|
+
Log.info("Allow forwarding to #{namespace.colorize(:cyan)} from #{network.nic.colorize(:cyan)}", newline:false)
|
271
|
+
Sys.exec_status("iptables -A FORWARD -i #{network.nic} -o #{host_veth.name} -j ACCEPT")
|
272
|
+
end
|
273
|
+
if !`iptables -S`.include?("-A FORWARD -i #{host_veth.name}")
|
274
|
+
Log.info("Allow forwarding from #{namespace.colorize(:cyan)} to #{network.nic.colorize(:cyan)}", newline:false)
|
275
|
+
Sys.exec_status("iptables -A FORWARD -i #{host_veth.name} -o #{network.nic} -j ACCEPT")
|
276
|
+
end
|
175
277
|
end
|
176
278
|
|
177
|
-
# Configure nameserver to use in
|
279
|
+
# Configure nameserver to use in Network namespace
|
178
280
|
namespace_conf = File.join("/etc/netns", namespace)
|
179
281
|
if !File.exists?(namespace_conf) && network.nameservers
|
180
282
|
Log.info("Creating nameserver config #{namespace_conf}", newline:false)
|
@@ -187,40 +289,48 @@ module Net
|
|
187
289
|
end
|
188
290
|
|
189
291
|
# Delete the given network namespace
|
190
|
-
#
|
292
|
+
# Params can be given as ordered positional args or named args
|
293
|
+
#
|
294
|
+
# @param namespace [String] name to use when deleting it
|
191
295
|
# @param host_veth [Veth] describes the veth to create for the host side
|
192
|
-
# @param
|
193
|
-
|
296
|
+
# @param guest_veth [Veth] describes the veth to create for the guest side
|
297
|
+
# @param network [Network] describes the network to share.
|
298
|
+
# If the nic param is nil NAT is not enabled. If nic is true then the primary nic is dynamically
|
299
|
+
# looked up else use user given If nameservers are not given the host nameservers will be used
|
300
|
+
def delete_namespace(namespace, *args)
|
301
|
+
host_veth, guest_veth, network = self.namespace_details(namespace, args)
|
194
302
|
|
195
|
-
# Remove nameserver config for
|
303
|
+
# Remove nameserver config for network namespace
|
196
304
|
namespace_conf = File.join("/etc/netns", namespace)
|
197
305
|
if File.exists?(namespace_conf)
|
198
|
-
Log.info("Removing nameserver config #{namespace_conf}", newline:false)
|
306
|
+
Log.info("Removing nameserver config #{namespace_conf.colorize(:cyan)}", newline:false)
|
199
307
|
Sys.exec_status("rm -rf #{namespace_conf}")
|
200
308
|
end
|
201
309
|
|
202
310
|
# Remove NAT and iptables forwarding allowances
|
203
|
-
if
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
311
|
+
if network.nic
|
312
|
+
if `iptables -t nat -S`.include?(File.join(network.subnet, network.cidr))
|
313
|
+
Log.info("Removing NAT on host for namespace #{File.join(network.subnet, network.cidr).colorize(:cyan)}", newline:false)
|
314
|
+
Sys.exec_status("iptables -t nat -D POSTROUTING -s #{File.join(network.subnet, network.cidr)} -o #{network.nic} -j MASQUERADE")
|
315
|
+
end
|
316
|
+
if `iptables -S`.include?("-A FORWARD -i #{network.nic}")
|
317
|
+
Log.info("Remove forwarding to #{namespace.colorize(:cyan)} from #{host_veth.ip.colorize(:cyan)}", newline:false)
|
318
|
+
Sys.exec_status("iptables -D FORWARD -i #{network.nic} -o #{host_veth.name} -j ACCEPT")
|
319
|
+
end
|
320
|
+
if `iptables -S`.include?("-A FORWARD -i #{host_veth.name}")
|
321
|
+
Log.info("Remove forwarding from #{namespace.colorize(:cyan)} to #{host_veth.ip.colorize(:cyan)}", newline:false)
|
322
|
+
Sys.exec_status("iptables -D FORWARD -i #{host_veth.name} -o #{network.nic} -j ACCEPT")
|
323
|
+
end
|
214
324
|
end
|
215
325
|
|
216
|
-
# Remove virtual ethernet interfaces
|
326
|
+
# Remove veths (virtual ethernet interfaces)
|
217
327
|
if `ip a`.include?(host_veth.name)
|
218
|
-
Log.info("Removing veth interface #{host_veth.name.colorize(:cyan)} for
|
328
|
+
Log.info("Removing veth interface #{host_veth.name.colorize(:cyan)} for namespace", newline:false)
|
219
329
|
Sys.exec_status("ip link delete #{host_veth.name}")
|
220
330
|
end
|
221
331
|
|
222
|
-
# Remove namespace
|
223
|
-
if
|
332
|
+
# Remove namespace
|
333
|
+
if self.namespaces.include?(namespace)
|
224
334
|
Log.info("Removing namespace #{namespace.colorize(:cyan)}", newline:false)
|
225
335
|
Sys.exec_status("ip netns delete #{namespace}")
|
226
336
|
end
|
@@ -230,7 +340,8 @@ module Net
|
|
230
340
|
# @param namespace [String] to execut within
|
231
341
|
# @param cmd [String] command to execute
|
232
342
|
# @param proxy [String] to use rather than default
|
233
|
-
def namespace_exec(namespace, cmd,
|
343
|
+
def namespace_exec(namespace, cmd, *args)
|
344
|
+
proxy = args.any? ? args.first.to_s : nil
|
234
345
|
return `ip netns exec #{namespace} bash -c '#{self.proxy_export(proxy)}#{cmd}'`
|
235
346
|
end
|
236
347
|
end
|
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.
|
4
|
+
version: 0.0.119
|
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-
|
11
|
+
date: 2018-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.8.22
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.8.22
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -89,6 +89,7 @@ files:
|
|
89
89
|
- LICENSE
|
90
90
|
- README.md
|
91
91
|
- lib/nub.rb
|
92
|
+
- lib/nub/:w
|
92
93
|
- lib/nub/commander.rb
|
93
94
|
- lib/nub/config.rb
|
94
95
|
- lib/nub/core.rb
|