vagrant-share 0.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7aef04145a45e96678767bdc41b5533223dabf38bb925fb2ea976d50e121ce0
4
- data.tar.gz: 61b414dcd6dc9e2f590f2b46e779a9c66d0630657fa05a976d91e7491fe505b6
3
+ metadata.gz: 9ce4b9c14ff2751cfa2baf8d40a1ee8586842edd4d8bd10fbae68b3a2a995240
4
+ data.tar.gz: ba5d360f4b7c05eb0f742e51e887a6325cd85cd91ae01e4805458209fec46172
5
5
  SHA512:
6
- metadata.gz: 91014f58f732f32e674f783ae835ef4ceb43aa3494b0f806ce6a4a466919d2fad26a11a8459c9de770b0c9a0f6eb6559cbfaa82f6a98a22ea980de3b49fafbbb
7
- data.tar.gz: '0082ce32c15fcdaf5e6c818a3801a8c27a4459b59cb6c1598faa570d0e21d9fe0e12e9b6e8e93ca063b3fc2ce16267701d7089bdf4400aa1c6236ededdd91aa3'
6
+ metadata.gz: c5eaa589dabf2a75d9e01705b381c21419b7eeee2309c8fb9cd965f45c64a18222b368ce394353c155b19e71c5b1e703d0294a4f0d64e086c00c1268a1ab69e9
7
+ data.tar.gz: b93bf2c432942c24ccaca7a6764f0ebe601a9875d667254d7d35035a0017027f8c770d4552509f2aba3f22e0469a3ade381654a689cf4cd297a97b5d61353e3b
@@ -0,0 +1,18 @@
1
+ require "pathname"
2
+ require "vagrant-share/plugin"
3
+
4
+ module VagrantPlugins
5
+ module Share
6
+ autoload :Command, "vagrant-share/command"
7
+ autoload :Errors, "vagrant-share/errors"
8
+ autoload :Helper, "vagrant-share/helper"
9
+ autoload :VERSION, "vagrant-share/version"
10
+
11
+ # This returns the path to the source of this plugin.
12
+ #
13
+ # @return [Pathname]
14
+ def self.source_root
15
+ @source_root ||= Pathname.new(File.expand_path("../../", __FILE__))
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,89 @@
1
+ module VagrantPlugins
2
+ module Share
3
+ module Cap
4
+ class TinyCore
5
+
6
+ DEFAULT_SHARE_PASSWORD="VAGRANT_SHARE_DEFAULT_PASSWORD".freeze
7
+
8
+ def self.share_proxy(machine, options={})
9
+ proxy_port = options.fetch(:proxy, 31338)
10
+ proxied_ports = options.fetch(:forwards, [])
11
+ shared_password = options[:shared_password] || DEFAULT_SHARE_PASSWORD
12
+
13
+ machine.communicate.tap do |comm|
14
+ comm.sudo("sysctl -w net.ipv4.ip_forward=1")
15
+ comm.sudo("/usr/local/sbin/iptables -t filter -I INPUT -p tcp -i eth0 --dport #{proxy_port} -j ACCEPT")
16
+ comm.sudo("/usr/local/sbin/iptables -t nat -A OUTPUT -d `route | grep default | awk '{print $2}'`/32 -j RETURN")
17
+ proxied_ports.each do |port_number|
18
+ comm.sudo("/usr/local/sbin/iptables -t nat -A OUTPUT -p tcp --dport #{port_number} -j DNAT --to-destination `route | grep default | awk '{print $2}'`:#{port_number}")
19
+ end
20
+ if options[:target]
21
+ comm.sudo("/usr/local/sbin/iptables -t nat -A OUTPUT -p tcp -j DNAT --to-destination #{options[:target]}")
22
+ end
23
+ comm.sudo("start-stop-daemon -b --start --exec /usr/local/bin/ss-server " \
24
+ "-- -password #{shared_password} -s :#{proxy_port}")
25
+ end
26
+ proxy_port
27
+ end
28
+
29
+ def self.connect_proxy(machine, vm_ip, proxy_port, **opts)
30
+ if opts[:type].to_s == "standalone"
31
+ redsocks_conf = REDSOCKS_CONF.
32
+ gsub("%PROXY_IP%", "127.0.0.1").
33
+ gsub("%PROXY_PORT%", "31339")
34
+ else
35
+ ip_parts = vm_ip.split(".")
36
+ ip_parts[3] = "1"
37
+ host_ip = ip_parts.join(".")
38
+
39
+ redsocks_conf = REDSOCKS_CONF.
40
+ gsub("%PROXY_IP%", host_ip).
41
+ gsub("%PROXY_PORT%", proxy_port.to_s)
42
+ end
43
+ shared_password = opts[:shared_password] || DEFAULT_SHARE_PASSWORD
44
+
45
+ machine.communicate.tap do |comm|
46
+ comm.sudo("sysctl -w net.ipv4.ip_forward=1")
47
+ comm.sudo("/usr/local/sbin/iptables -t nat -A PREROUTING -i eth1 -p tcp -j REDIRECT --to-ports 31338")
48
+ comm.sudo("/usr/local/sbin/iptables -A INPUT -i eth1 -j ACCEPT")
49
+
50
+ comm.sudo("rm -f ~/redsocks.conf")
51
+ redsocks_conf.split("\n").each do |line|
52
+ comm.sudo("echo '#{line}' >> ~/redsocks.conf")
53
+ end
54
+
55
+ if opts[:type].to_s == "standalone"
56
+ comm.sudo("start-stop-daemon -b --start --exec /usr/local/bin/ss-client " \
57
+ "-- -socks :31339 -password #{shared_password} -c #{vm_ip}:#{proxy_port}")
58
+ end
59
+
60
+ comm.sudo(
61
+ "start-stop-daemon -b --start --exec /usr/local/bin/redsocks " \
62
+ "-- -c ~/redsocks.conf")
63
+ end
64
+ end
65
+
66
+ # This is the configuration for redsocks that we use to proxy
67
+ REDSOCKS_CONF = <<EOF
68
+ base {
69
+ log_debug = on;
70
+ log_info = on;
71
+ log = "file:/root/redsocks.log";
72
+ daemon = off;
73
+ redirector = iptables;
74
+ }
75
+
76
+ redsocks {
77
+ local_ip = 0.0.0.0;
78
+ local_port = 31338;
79
+ ip = %PROXY_IP%;
80
+ port = %PROXY_PORT%;
81
+ type = socks5;
82
+ }
83
+ EOF
84
+
85
+
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,9 @@
1
+ module VagrantPlugins
2
+ module Share
3
+ module Command
4
+ autoload :Connect, "vagrant-share/command/connect"
5
+ autoload :Ngrok, "vagrant-share/command/ngrok"
6
+ autoload :Share, "vagrant-share/command/share"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,82 @@
1
+ require "ipaddr"
2
+ require "openssl"
3
+ require "pathname"
4
+ require "thread"
5
+ require "tempfile"
6
+ require "uri"
7
+
8
+ require "log4r"
9
+
10
+ require "vagrant/util/ssh"
11
+
12
+ module VagrantPlugins
13
+ module Share
14
+ module Command
15
+ class Connect < Vagrant.plugin("2", "command")
16
+
17
+ include Ngrok::Connect
18
+
19
+ def self.synopsis
20
+ "connect to a remotely shared Vagrant environment"
21
+ end
22
+
23
+ def execute
24
+ @logger = Log4r::Logger.new("vagrant::share::command::connect")
25
+
26
+ options = {}
27
+
28
+ opts = OptionParser.new do |o|
29
+ o.banner = "Usage: vagrant connect NAME"
30
+ o.separator ""
31
+ o.separator "Gives you access to any Vagrant environment shared using "
32
+ o.separator "`vagrant share`. The NAME parameter should be the unique name"
33
+ o.separator "that was outputted with `vagrant share` on the remote side."
34
+ o.separator ""
35
+ o.separator "Vagrant will give you an IP that you can use to access the"
36
+ o.separator "remote environment. You'll be able to access any ports that"
37
+ o.separator "the shared environment has authorized."
38
+ o.separator ""
39
+ o.on("--disable-static-ip", "No static IP, only a SOCKS proxy") do |s|
40
+ options[:disable_static_ip] = s
41
+ end
42
+
43
+ o.on("--static-ip IP", String, "Manually override static IP chosen") do |ip|
44
+ begin
45
+ IPAddr.new(ip)
46
+ rescue IPAddr::InvalidAddressError
47
+ raise Errors::IPInvalid, ip: ip
48
+ end
49
+
50
+ options[:static_ip] = ip
51
+ end
52
+
53
+ o.on("--ssh", "SSH into the remote machine") do |ssh|
54
+ options[:ssh] = ssh
55
+ end
56
+
57
+ o.on("--driver DRIVER", "Deprecated option for compatibility") do |driver|
58
+ options[:driver] = driver
59
+ end
60
+
61
+ o.on("--share-password", "Custom share password") do |p|
62
+ options[:share_password] = p
63
+ end
64
+ end
65
+
66
+ # Parse the options
67
+ argv = parse_options(opts)
68
+ return if !argv
69
+ if argv.empty? || argv.length > 1
70
+ raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp
71
+ end
72
+
73
+ if options[:ssh]
74
+ return execute_ssh(argv, options)
75
+ else
76
+ return execute_connect(argv, options)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,14 @@
1
+ module VagrantPlugins
2
+ module Share
3
+ module Command
4
+ module Ngrok
5
+ # Default ngrok endpoint for TCP style connections
6
+ DEFAULT_NGROK_TCP_ENDPOINT = "0.tcp.ngrok.io".freeze
7
+ NGROK_TCP_DOMAIN = "tcp.ngrok.io".freeze
8
+
9
+ autoload :Connect, "vagrant-share/command/ngrok/connect"
10
+ autoload :Share, "vagrant-share/command/ngrok/share"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,236 @@
1
+ module VagrantPlugins
2
+ module Share
3
+ module Command
4
+ module Ngrok
5
+ module Connect
6
+ # Start the ngrok based connect
7
+ #
8
+ # @param [Array<String>] argv CLI arguments
9
+ # @param [Hash] options CLI options
10
+ def connect_to_share(argv, options)
11
+ if options[:retrap_int]
12
+ halt_queue = Queue.new
13
+ wait_runner = Thread.new do
14
+ until halt_queue.pop == :halt
15
+ @logger.debug("Connect wait runner watching for halt.")
16
+ end
17
+ @logger.debug("Connect wait runner received halt instruction.")
18
+ :halt
19
+ end
20
+ Helper.signal_retrap("INT") do
21
+ halt_queue << :halt
22
+ end
23
+ end
24
+
25
+ name, host_info = argv.first.to_s.split("@")
26
+ proxy_port, api_port = name.split(":").map do |word_pair|
27
+ Helper.dewordify(word_pair.split("_"))
28
+ end
29
+ host_num = host_info.to_s.empty? ? "0" : Helper.dewordify(host_info.split("_")).to_s
30
+
31
+ # Determine the static IP to use for the VM, if we're using one.
32
+ ip = nil
33
+ ip_f = nil
34
+ if !options[:disable_static_ip]
35
+ @logger.info("Acquiring IP for connect VM")
36
+ ip, ip_f = Helper.acquire_ip(@env, options[:static_ip])
37
+ if ip == nil
38
+ if options[:static_ip]
39
+ raise Errors::IPInUse, ip: options[:static_ip]
40
+ else
41
+ raise Errors::IPCouldNotAutoAcquire
42
+ end
43
+ end
44
+ @logger.info("Connect VM IP will be: #{ip}")
45
+ end
46
+
47
+ # Get the proxy machine we'll start
48
+ machine = Helper.share_machine(@env, ip: ip)
49
+ ui = machine.ui
50
+ proxy_ui = machine.ui.dup
51
+ proxy_ui.opts[:bold] = false
52
+ proxy_ui.opts[:prefix_spaces] = true
53
+
54
+ ui.output(I18n.t("vagrant_share.connecting", name: name))
55
+
56
+ # Check for box updates if we have a box already...
57
+ if machine.box
58
+ ui.output(I18n.t("vagrant_share.box_update_check"))
59
+ begin
60
+ if machine.box.has_update?
61
+ ui.detail(I18n.t("vagrant_share.box_update"))
62
+
63
+ # There is an update! Delete the previous box to force
64
+ # an update.
65
+ machine.box.destroy!
66
+ machine.box = nil
67
+ else
68
+ ui.detail(I18n.t("vagrant_share.box_up_to_date"))
69
+ end
70
+ rescue => e
71
+ @logger.warn("failed to check for box update: #{e}")
72
+ end
73
+ end
74
+ if options[:driver].to_s.include?("ngrok.io")
75
+ proxy_endpoint = options[:driver]
76
+ else
77
+ proxy_endpoint = "#{host_num}.#{NGROK_TCP_DOMAIN}"
78
+ end
79
+
80
+ # Start the machine
81
+ ui.output(I18n.t("vagrant_share.starting_proxy_vm"))
82
+ machine.with_ui(proxy_ui) do
83
+ machine.action(:up)
84
+ machine.guest.capability(:connect_proxy, proxy_endpoint, proxy_port,
85
+ type: :standalone
86
+ )
87
+ end
88
+
89
+ yield name, ui, ip, proxy_port, api_port
90
+
91
+ # NOTE: Use a timed join on wait thread to prevent deadlock being claimed
92
+ if wait_runner
93
+ until wait_runner.join(30) == :halt
94
+ if Helper.ping_share(ip, api_port)
95
+ @logger.debug("Validated share connection alive. Waiting for halt.")
96
+ else
97
+ @logger.error("Share connection state has been lost. Halting connect.")
98
+ ui.error(I18n.t("vagrant_share.ngrok.connection_lost", name: name))
99
+ Process.kill("INT", Process.pid)
100
+ end
101
+ end
102
+ end
103
+ ensure
104
+ # If we have a machine, make sure we destroy it
105
+ if machine
106
+ machine.action(:destroy, force_confirm_destroy: true)
107
+ end
108
+ # If we acquired an IP, make sure we close and delete that file
109
+ if ip_f
110
+ ip_f.close
111
+ File.delete(ip_f.path) rescue nil
112
+ end
113
+ end
114
+
115
+ # Perform connection to share
116
+ #
117
+ # @param [Array<String>] argv CLI arguments
118
+ # @param [Hash] options CLI options
119
+ def execute_connect(argv, options)
120
+ opts = options.merge(retrap_int: true)
121
+ connect_to_share(argv, opts) do |name, ui, ip, proxy_port, api_port|
122
+ begin
123
+ share = Helper.share_info("share-info", ip, api_port)
124
+ rescue => e
125
+ @logger.error("Failed to establish connection to share `#{name}`." \
126
+ "#{e.class}: #{e}")
127
+ raise Errors::ShareNotFound, name: name
128
+ end
129
+ ui.output(I18n.t("vagrant_share.looking_up", name: name))
130
+
131
+ share["ports"] ||= []
132
+ if !share["ports"].empty?
133
+ ui.detail(I18n.t("vagrant_share.connect_restricted_ports") + "\n ")
134
+ share["ports"].each do |port|
135
+ next if port.to_s == proxy_port.to_s
136
+ ui.detail("Port: #{port}")
137
+ end
138
+ ui.detail(" ")
139
+ end
140
+
141
+ if share["has_private_key"]
142
+ ui.detail(I18n.t("vagrant_share.connect_ssh_available"))
143
+ ui.detail(" ")
144
+ end
145
+
146
+ # Let the user know we connected and how to connect
147
+ ui.success(I18n.t("vagrant_share.started_connect"))
148
+ ui.success(I18n.t(
149
+ "vagrant_share.connect_socks_port", port: proxy_port.to_s))
150
+ ui.success(I18n.t("vagrant_share.connect_ip", ip: ip))
151
+ ui.success(" ")
152
+ ui.success(I18n.t("vagrant_share.connect_info"))
153
+ end
154
+ end
155
+
156
+ # Perform SSH connection to share
157
+ #
158
+ # @param [Array<String>] argv CLI arguments
159
+ # @param [Hash] options CLI options
160
+ def execute_ssh(argv, options)
161
+ private_key_path = nil
162
+ connect_to_share(argv, options) do |name, ui, ip, proxy_port, api_port|
163
+ # Determine information about this share
164
+ ui.output(I18n.t("vagrant_share.looking_up", name: name))
165
+ share = Helper.share_info("connect-ssh", ip, api_port)
166
+
167
+ if !share["has_private_key"]
168
+ raise Errors::SSHNotShared,
169
+ name: name
170
+ end
171
+
172
+ if share["private_key_password"]
173
+ ui.ask(
174
+ I18n.t("vagrant_share.connect_password_required"),
175
+ color: :yellow)
176
+ end
177
+
178
+ # Get the private key
179
+ private_key = share["ssh_key"]
180
+ if share["private_key_password"]
181
+ while true
182
+ password = nil
183
+ while !password
184
+ password = ui.ask(
185
+ "#{I18n.t("vagrant_share.connect_password_prompt")} ",
186
+ echo: false)
187
+ end
188
+
189
+ begin
190
+ private_key = OpenSSL::PKey::RSA.new(private_key, password)
191
+ private_key = private_key.to_pem
192
+ password = nil
193
+ break
194
+ rescue OpenSSL::PKey::RSAError
195
+ ui.error(I18n.t("vagrant_share.connect_invalid_password"))
196
+ end
197
+ end
198
+ end
199
+
200
+ private_key_file = Tempfile.new("vagrant-connect-key")
201
+ private_key_path = Pathname.new(private_key_file.path)
202
+ private_key_file.write(private_key)
203
+ private_key_file.close
204
+
205
+ # Nil out the private key so it can be GC'd
206
+ private_key = nil
207
+
208
+ # In 45 seconds, delete the private key file. 45 seconds
209
+ # should be long enough for SSH to properly connect.
210
+ key_thr = Thread.new do
211
+ sleep 45
212
+ private_key_path.unlink rescue nil
213
+ end
214
+
215
+ # Configure SSH
216
+ ssh_info = {
217
+ host: ip,
218
+ port: share["ssh_port"],
219
+ username: share["ssh_username"],
220
+ private_key_path: [private_key_path.to_s],
221
+ }
222
+ ui.output(I18n.t("vagrant_share.executing_ssh"))
223
+ Vagrant::Util::SSH.check_key_permissions(private_key_path)
224
+ Vagrant::Util::SSH.exec(ssh_info, subprocess: true)
225
+ end
226
+ ensure
227
+ # If the private key is still around, delete it
228
+ if private_key_path && private_key_path.file?
229
+ private_key_path.unlink rescue nil
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end