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