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 +4 -4
- data/lib/vagrant-share.rb +18 -0
- data/lib/vagrant-share/cap/tinycore.rb +89 -0
- data/lib/vagrant-share/command.rb +9 -0
- data/lib/vagrant-share/command/connect.rb +82 -0
- data/lib/vagrant-share/command/ngrok.rb +14 -0
- data/lib/vagrant-share/command/ngrok/connect.rb +236 -0
- data/lib/vagrant-share/command/ngrok/share.rb +500 -0
- data/lib/vagrant-share/command/share.rb +153 -0
- data/lib/vagrant-share/errors.rb +96 -0
- data/lib/vagrant-share/helper.rb +488 -0
- data/lib/vagrant-share/helper/api.rb +46 -0
- data/lib/vagrant-share/helper/word_list.rb +550 -0
- data/lib/vagrant-share/plugin.rb +49 -0
- data/lib/vagrant-share/version.rb +5 -0
- data/locales/en.yml +297 -0
- data/version.txt +1 -0
- metadata +58 -13
- data/vagrant-share.gemspec +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ce4b9c14ff2751cfa2baf8d40a1ee8586842edd4d8bd10fbae68b3a2a995240
|
4
|
+
data.tar.gz: ba5d360f4b7c05eb0f742e51e887a6325cd85cd91ae01e4805458209fec46172
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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,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
|