vagrant-share 0.0.1 → 2.0.0

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 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