messhy 0.4.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 +7 -0
- data/LICENSE +23 -0
- data/README.md +296 -0
- data/exe/messhy +6 -0
- data/lib/messhy/cli.rb +280 -0
- data/lib/messhy/configuration.rb +87 -0
- data/lib/messhy/generators/messhy/install_generator.rb +47 -0
- data/lib/messhy/health_checker.rb +203 -0
- data/lib/messhy/host_trust_manager.rb +139 -0
- data/lib/messhy/installer.rb +334 -0
- data/lib/messhy/mesh_builder.rb +70 -0
- data/lib/messhy/railtie.rb +18 -0
- data/lib/messhy/ssh_executor.rb +305 -0
- data/lib/messhy/version.rb +5 -0
- data/lib/messhy/wireguard_status_parser.rb +54 -0
- data/lib/messhy.rb +21 -0
- data/lib/tasks/messhy.rake +32 -0
- data/templates/wg0.conf.erb +24 -0
- metadata +162 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'sshkit'
|
|
4
|
+
require 'sshkit/dsl'
|
|
5
|
+
require 'stringio'
|
|
6
|
+
|
|
7
|
+
module Messhy
|
|
8
|
+
class SSHExecutor
|
|
9
|
+
include SSHKit::DSL
|
|
10
|
+
|
|
11
|
+
attr_reader :config
|
|
12
|
+
|
|
13
|
+
def initialize(config)
|
|
14
|
+
@config = config
|
|
15
|
+
setup_sshkit
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def execute_on_node(node_name, &)
|
|
19
|
+
node_config = config.node_config(node_name)
|
|
20
|
+
raise Error, "Node not found: #{node_name}" unless node_config
|
|
21
|
+
|
|
22
|
+
host = host_for(node_name, node_config)
|
|
23
|
+
on(host, &)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def execute_on_all_nodes(skip: nil, &)
|
|
27
|
+
hosts = config.each_node.with_object([]) do |(node_name, node_config), collection|
|
|
28
|
+
next if skip && node_name == skip
|
|
29
|
+
|
|
30
|
+
collection << host_for(node_name, node_config)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
return if hosts.empty?
|
|
34
|
+
|
|
35
|
+
on(hosts, in: :parallel, &)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def install_wireguard(node_name)
|
|
39
|
+
execute_on_node(node_name) do
|
|
40
|
+
# Check if WireGuard is already installed
|
|
41
|
+
if test('[ -f /usr/bin/wg ]')
|
|
42
|
+
info 'WireGuard already installed'
|
|
43
|
+
else
|
|
44
|
+
info 'Installing WireGuard...'
|
|
45
|
+
execute :sudo, 'apt-get', 'update', '-qq'
|
|
46
|
+
execute :sudo, 'DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install', '-y', '-qq', 'wireguard',
|
|
47
|
+
'iputils-ping'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Install ping if not available
|
|
51
|
+
unless test('which', 'ping', raise_on_error: false)
|
|
52
|
+
info 'Installing ping utility...'
|
|
53
|
+
execute :sudo, 'apt-get', 'update', '-qq', raise_on_error: false
|
|
54
|
+
execute :sudo, 'DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install', '-y', '-qq', 'iputils-ping',
|
|
55
|
+
raise_on_error: false
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def generate_keypair_on_node(node_name)
|
|
61
|
+
keypair = {}
|
|
62
|
+
execute_on_node(node_name) do
|
|
63
|
+
keypair[:private_key] = capture('wg', 'genkey').strip
|
|
64
|
+
keypair[:public_key] = capture(:echo, keypair[:private_key], '|', 'wg', 'pubkey').strip
|
|
65
|
+
end
|
|
66
|
+
keypair
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def generate_psk_on_node(node_name)
|
|
70
|
+
psk = nil
|
|
71
|
+
execute_on_node(node_name) do
|
|
72
|
+
psk = capture('wg', 'genpsk').strip
|
|
73
|
+
end
|
|
74
|
+
psk
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def install_wireguard_on_all_nodes(skip: nil)
|
|
78
|
+
execute_on_all_nodes(skip: skip) do
|
|
79
|
+
# Check if WireGuard is already installed
|
|
80
|
+
if test('[ -f /usr/bin/wg ]')
|
|
81
|
+
info 'WireGuard already installed'
|
|
82
|
+
else
|
|
83
|
+
info 'Installing WireGuard...'
|
|
84
|
+
execute :sudo, 'apt-get', 'update', '-qq'
|
|
85
|
+
execute :sudo, 'DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install', '-y', '-qq', 'wireguard',
|
|
86
|
+
'iputils-ping'
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Install ping if not available
|
|
90
|
+
unless test('which', 'ping', raise_on_error: false)
|
|
91
|
+
info 'Installing ping utility...'
|
|
92
|
+
execute :sudo, 'apt-get', 'update', '-qq', raise_on_error: false
|
|
93
|
+
execute :sudo, 'DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install', '-y', '-qq', 'iputils-ping',
|
|
94
|
+
raise_on_error: false
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def upload_config(node_name, config_content)
|
|
100
|
+
execute_on_node(node_name) do
|
|
101
|
+
# Create temporary file
|
|
102
|
+
temp_file = '/tmp/wg0.conf'
|
|
103
|
+
upload! StringIO.new(config_content), temp_file
|
|
104
|
+
|
|
105
|
+
# Move to /etc/wireguard with proper permissions
|
|
106
|
+
execute :sudo, 'mv', temp_file, '/etc/wireguard/wg0.conf'
|
|
107
|
+
execute :sudo, 'chmod', '600', '/etc/wireguard/wg0.conf'
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def upload_and_start_configs(configs)
|
|
112
|
+
hosts = configs.filter_map do |node_name, config_content|
|
|
113
|
+
node_config = config.node_config(node_name)
|
|
114
|
+
next unless node_config
|
|
115
|
+
|
|
116
|
+
host = host_for(node_name, node_config)
|
|
117
|
+
manage_property(host.properties, :config_content, config_content)
|
|
118
|
+
host
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
return if hosts.empty?
|
|
122
|
+
|
|
123
|
+
executor = self
|
|
124
|
+
|
|
125
|
+
on hosts, in: :parallel do |host|
|
|
126
|
+
properties = host.properties
|
|
127
|
+
config_content = executor.send(:manage_property, properties, :config_content)
|
|
128
|
+
temp_file = '/tmp/wg0.conf'
|
|
129
|
+
upload! StringIO.new(config_content), temp_file
|
|
130
|
+
execute :sudo, 'mv', temp_file, '/etc/wireguard/wg0.conf'
|
|
131
|
+
execute :sudo, 'chmod', '600', '/etc/wireguard/wg0.conf'
|
|
132
|
+
execute :sudo, 'systemctl', 'enable', 'wg-quick@wg0'
|
|
133
|
+
if test('systemctl is-active wg-quick@wg0')
|
|
134
|
+
execute :sudo, 'systemctl', 'restart', 'wg-quick@wg0'
|
|
135
|
+
else
|
|
136
|
+
execute :sudo, 'systemctl', 'start', 'wg-quick@wg0'
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def enable_and_start_wireguard(node_name)
|
|
142
|
+
execute_on_node(node_name) do
|
|
143
|
+
# Enable systemd service
|
|
144
|
+
execute :sudo, 'systemctl', 'enable', 'wg-quick@wg0'
|
|
145
|
+
|
|
146
|
+
# Restart WireGuard
|
|
147
|
+
if test('systemctl is-active wg-quick@wg0')
|
|
148
|
+
execute :sudo, 'systemctl', 'restart', 'wg-quick@wg0'
|
|
149
|
+
else
|
|
150
|
+
execute :sudo, 'systemctl', 'start', 'wg-quick@wg0'
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def get_wireguard_status(node_name)
|
|
156
|
+
result = nil
|
|
157
|
+
execute_on_node(node_name) do
|
|
158
|
+
result = capture(:sudo, 'wg', 'show', 'wg0')
|
|
159
|
+
end
|
|
160
|
+
result
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def ping_node_from(source_node, target_ip)
|
|
164
|
+
success = false
|
|
165
|
+
execute_on_node(source_node) do
|
|
166
|
+
if test('which', 'ping', raise_on_error: false)
|
|
167
|
+
success = test('timeout', '3', 'ping', '-c', '1', '-W', '1', '-I', 'wg0', target_ip, raise_on_error: false)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
success
|
|
171
|
+
rescue StandardError
|
|
172
|
+
false
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def test_tcp_connectivity(source_node, target_ip, port = 22)
|
|
176
|
+
success = false
|
|
177
|
+
execute_on_node(source_node) do
|
|
178
|
+
success = test('timeout', '2', 'bash', '-c',
|
|
179
|
+
"exec 3<>/dev/tcp/#{target_ip}/#{port} 2>&1 && exec 3<&- && exec 3>&-", raise_on_error: false)
|
|
180
|
+
end
|
|
181
|
+
success
|
|
182
|
+
rescue StandardError
|
|
183
|
+
false
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def restart_wireguard(node_name)
|
|
187
|
+
execute_on_node(node_name) do
|
|
188
|
+
# Stop service first
|
|
189
|
+
if test('systemctl is-active wg-quick@wg0', raise_on_error: false)
|
|
190
|
+
execute :sudo, 'systemctl', 'stop', 'wg-quick@wg0'
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Remove interface if it exists
|
|
194
|
+
if test('[ -d /sys/class/net/wg0 ]', raise_on_error: false)
|
|
195
|
+
execute :sudo, 'ip', 'link', 'delete', 'wg0', raise_on_error: false
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Start fresh
|
|
199
|
+
execute :sudo, 'systemctl', 'start', 'wg-quick@wg0'
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def stop_wireguard(node_name)
|
|
204
|
+
execute_on_node(node_name) do
|
|
205
|
+
execute :sudo, 'systemctl', 'stop', 'wg-quick@wg0' if test('systemctl is-active wg-quick@wg0')
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def purge_wireguard(node_name)
|
|
210
|
+
execute_on_node(node_name) do
|
|
211
|
+
# Stop and disable service
|
|
212
|
+
if test('systemctl is-active wg-quick@wg0', raise_on_error: false)
|
|
213
|
+
execute :sudo, 'systemctl', 'stop', 'wg-quick@wg0', raise_on_error: false
|
|
214
|
+
end
|
|
215
|
+
if test('systemctl is-enabled wg-quick@wg0', raise_on_error: false)
|
|
216
|
+
execute :sudo, 'systemctl', 'disable', 'wg-quick@wg0', raise_on_error: false
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Remove interface
|
|
220
|
+
if test('[ -d /sys/class/net/wg0 ]', raise_on_error: false)
|
|
221
|
+
execute :sudo, 'ip', 'link', 'delete', 'wg0', raise_on_error: false
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Remove config
|
|
225
|
+
if test('[ -f /etc/wireguard/wg0.conf ]', raise_on_error: false)
|
|
226
|
+
execute :sudo, 'rm', '-f', '/etc/wireguard/wg0.conf', raise_on_error: false
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
private
|
|
232
|
+
|
|
233
|
+
def install_wireguard_packages
|
|
234
|
+
# Check if WireGuard is already installed
|
|
235
|
+
if test('[ -f /usr/bin/wg ]')
|
|
236
|
+
info 'WireGuard already installed'
|
|
237
|
+
else
|
|
238
|
+
info 'Installing WireGuard...'
|
|
239
|
+
execute :sudo, 'apt-get', 'update', '-qq'
|
|
240
|
+
execute :sudo, 'DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install', '-y', '-qq', 'wireguard',
|
|
241
|
+
'iputils-ping'
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Install ping if not available
|
|
245
|
+
return if test('which', 'ping', raise_on_error: false)
|
|
246
|
+
|
|
247
|
+
info 'Installing ping utility...'
|
|
248
|
+
execute :sudo, 'apt-get', 'update', '-qq', raise_on_error: false
|
|
249
|
+
execute :sudo, 'DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install', '-y', '-qq', 'iputils-ping',
|
|
250
|
+
raise_on_error: false
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def setup_sshkit
|
|
254
|
+
SSHKit.config.output_verbosity = Logger::INFO
|
|
255
|
+
SSHKit.config.use_format :pretty
|
|
256
|
+
|
|
257
|
+
SSHKit::Backend::Netssh.configure do |ssh|
|
|
258
|
+
ssh.ssh_options = build_ssh_options
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def build_ssh_options
|
|
263
|
+
options = {
|
|
264
|
+
forward_agent: false,
|
|
265
|
+
auth_methods: ['publickey'],
|
|
266
|
+
verify_host_key: config.verify_host_key_mode
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if File.exist?(config.ssh_key)
|
|
270
|
+
options[:keys] = [config.ssh_key]
|
|
271
|
+
options[:keys_only] = true
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
options
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def host_for(node_name, node_config)
|
|
278
|
+
host = SSHKit::Host.new(node_config['host'])
|
|
279
|
+
ssh_user = node_config['ssh_user'] || node_config['user'] || config.user
|
|
280
|
+
host.user = ssh_user if ssh_user
|
|
281
|
+
|
|
282
|
+
ssh_port = node_config['ssh_port'] || node_config['port']
|
|
283
|
+
host.port = ssh_port if ssh_port
|
|
284
|
+
|
|
285
|
+
if node_config['ssh_key']
|
|
286
|
+
keys = Array(node_config['ssh_key']).map { |path| File.expand_path(path) }
|
|
287
|
+
merged = (host.ssh_options || {}).merge(keys: keys, keys_only: true)
|
|
288
|
+
host.ssh_options = merged
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
manage_property(host.properties, :node_name, node_name)
|
|
292
|
+
host
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def manage_property(properties, key, value = nil)
|
|
296
|
+
if value.nil?
|
|
297
|
+
# Fetch mode
|
|
298
|
+
properties.respond_to?(:fetch) ? properties.fetch(key) : properties[key]
|
|
299
|
+
else
|
|
300
|
+
# Assign mode
|
|
301
|
+
properties.respond_to?(:set) ? properties.set(key, value) : properties[key] = value
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Messhy
|
|
4
|
+
module WireguardStatusParser
|
|
5
|
+
TIME_UNITS_IN_SECONDS = {
|
|
6
|
+
'second' => 1,
|
|
7
|
+
'minute' => 60,
|
|
8
|
+
'hour' => 3_600,
|
|
9
|
+
'day' => 86_400
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def extract_peer_block(status, target_ip)
|
|
15
|
+
status.split('peer:').drop(1).find do |block|
|
|
16
|
+
block.include?("allowed ips: #{target_ip}/32")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def parse_handshake_seconds(desc)
|
|
21
|
+
return nil if desc.strip.casecmp('(none)').zero?
|
|
22
|
+
|
|
23
|
+
matches = desc.scan(/(\d+)\s+(second|minute|hour|day)s?/i)
|
|
24
|
+
return nil if matches.empty?
|
|
25
|
+
|
|
26
|
+
matches.sum do |value, unit|
|
|
27
|
+
TIME_UNITS_IN_SECONDS[unit.downcase] * value.to_i
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def extract_handshake_time(peer_block)
|
|
32
|
+
return nil unless peer_block
|
|
33
|
+
|
|
34
|
+
desc = peer_block[/latest handshake:\s*(.+)/, 1]
|
|
35
|
+
return nil unless desc&.include?('ago')
|
|
36
|
+
|
|
37
|
+
parse_handshake_seconds(desc)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def extract_transfer_stats(peer_block)
|
|
41
|
+
rx = peer_block.match(/transfer: (.+?) received/)&.[](1) || '0 B'
|
|
42
|
+
tx = peer_block.match(/received, (.+?) sent/)&.[](1) || '0 B'
|
|
43
|
+
{ received: rx, sent: tx }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def extract_endpoint(peer_block)
|
|
47
|
+
peer_block[/endpoint: (.+?)$/, 1]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def extract_allowed_ips(peer_block)
|
|
51
|
+
peer_block[/allowed ips: (.+?)$/, 1]
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/messhy.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'messhy/version'
|
|
4
|
+
require_relative 'messhy/configuration'
|
|
5
|
+
require_relative 'messhy/wireguard_status_parser'
|
|
6
|
+
require_relative 'messhy/installer'
|
|
7
|
+
require_relative 'messhy/mesh_builder'
|
|
8
|
+
require_relative 'messhy/ssh_executor'
|
|
9
|
+
require_relative 'messhy/health_checker'
|
|
10
|
+
require_relative 'messhy/host_trust_manager'
|
|
11
|
+
require_relative 'messhy/cli'
|
|
12
|
+
|
|
13
|
+
module Messhy
|
|
14
|
+
class Error < StandardError; end
|
|
15
|
+
|
|
16
|
+
def self.root
|
|
17
|
+
File.expand_path('..', __dir__)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
require_relative 'messhy/railtie'
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
namespace :messhy do
|
|
2
|
+
desc 'Install Messhy configuration'
|
|
3
|
+
task :install do
|
|
4
|
+
system('rails generate messhy:install')
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
desc 'Deploy WireGuard VPN mesh to all nodes'
|
|
8
|
+
task :setup do
|
|
9
|
+
system('bundle exec messhy setup')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
desc 'Check VPN mesh connectivity'
|
|
13
|
+
task :health do
|
|
14
|
+
system('bundle exec messhy health')
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc 'Generate new WireGuard keys'
|
|
18
|
+
task :keygen do
|
|
19
|
+
system('bundle exec messhy keygen')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc 'Show mesh status'
|
|
23
|
+
task :status do
|
|
24
|
+
puts "\n🔒 WireGuard VPN Mesh Status\n\n"
|
|
25
|
+
system('bundle exec messhy status')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc 'Trust SSH host keys for all nodes'
|
|
29
|
+
task :trust_hosts do
|
|
30
|
+
system('bundle exec messhy trust-hosts')
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# WireGuard configuration for <%= node_name %>
|
|
2
|
+
# Generated by messhy
|
|
3
|
+
|
|
4
|
+
[Interface]
|
|
5
|
+
Address = <%= interface_ip %>/<%= prefix_length %>
|
|
6
|
+
PrivateKey = <%= private_key %>
|
|
7
|
+
ListenPort = <%= listen_port %>
|
|
8
|
+
MTU = <%= mtu %>
|
|
9
|
+
|
|
10
|
+
# Enable IP forwarding
|
|
11
|
+
PostUp = sysctl -w net.ipv4.ip_forward=1
|
|
12
|
+
PostDown = sysctl -w net.ipv4.ip_forward=0
|
|
13
|
+
|
|
14
|
+
<% peers.each do |peer| %>
|
|
15
|
+
# Peer: <%= peer[:name] %>
|
|
16
|
+
[Peer]
|
|
17
|
+
PublicKey = <%= peer[:public_key] %>
|
|
18
|
+
PresharedKey = <%= peer[:preshared_key] %>
|
|
19
|
+
AllowedIPs = <%= peer[:allowed_ips] %>
|
|
20
|
+
Endpoint = <%= peer[:endpoint] %>
|
|
21
|
+
PersistentKeepalive = <%= peer[:keepalive] %>
|
|
22
|
+
|
|
23
|
+
<% end %>
|
|
24
|
+
|
metadata
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: messhy
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.4.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- BoringCache
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: bcrypt_pbkdf
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '1.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '1.1'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: ed25519
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.3'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.3'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: sshkit
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.21'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.21'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: thor
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.3'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '1.3'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: minitest
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '5.0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '5.0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: rubocop
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '1.50'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '1.50'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: simplecov
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0.22'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0.22'
|
|
110
|
+
description: Sets up a full WireGuard VPN mesh across any VMs. Every node connects
|
|
111
|
+
directly to every other node for secure private networking.
|
|
112
|
+
email:
|
|
113
|
+
- oss@boringcache.com
|
|
114
|
+
executables:
|
|
115
|
+
- messhy
|
|
116
|
+
extensions: []
|
|
117
|
+
extra_rdoc_files: []
|
|
118
|
+
files:
|
|
119
|
+
- LICENSE
|
|
120
|
+
- README.md
|
|
121
|
+
- exe/messhy
|
|
122
|
+
- lib/messhy.rb
|
|
123
|
+
- lib/messhy/cli.rb
|
|
124
|
+
- lib/messhy/configuration.rb
|
|
125
|
+
- lib/messhy/generators/messhy/install_generator.rb
|
|
126
|
+
- lib/messhy/health_checker.rb
|
|
127
|
+
- lib/messhy/host_trust_manager.rb
|
|
128
|
+
- lib/messhy/installer.rb
|
|
129
|
+
- lib/messhy/mesh_builder.rb
|
|
130
|
+
- lib/messhy/railtie.rb
|
|
131
|
+
- lib/messhy/ssh_executor.rb
|
|
132
|
+
- lib/messhy/version.rb
|
|
133
|
+
- lib/messhy/wireguard_status_parser.rb
|
|
134
|
+
- lib/tasks/messhy.rake
|
|
135
|
+
- templates/wg0.conf.erb
|
|
136
|
+
homepage: https://github.com/boringcache/messhy
|
|
137
|
+
licenses:
|
|
138
|
+
- MIT
|
|
139
|
+
metadata:
|
|
140
|
+
homepage_uri: https://github.com/boringcache/messhy
|
|
141
|
+
source_code_uri: https://github.com/boringcache/messhy
|
|
142
|
+
documentation_uri: https://github.com/boringcache/messhy/blob/main/README.md
|
|
143
|
+
changelog_uri: https://github.com/boringcache/messhy/blob/main/CHANGELOG.md
|
|
144
|
+
rubygems_mfa_required: 'true'
|
|
145
|
+
rdoc_options: []
|
|
146
|
+
require_paths:
|
|
147
|
+
- lib
|
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - ">="
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: 3.4.0
|
|
153
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
|
+
requirements:
|
|
155
|
+
- - ">="
|
|
156
|
+
- !ruby/object:Gem::Version
|
|
157
|
+
version: '0'
|
|
158
|
+
requirements: []
|
|
159
|
+
rubygems_version: 3.6.7
|
|
160
|
+
specification_version: 4
|
|
161
|
+
summary: WireGuard VPN mesh for Ruby & Rails apps
|
|
162
|
+
test_files: []
|