lxdev 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/bin/lxdev +2 -5
  3. data/lib/lxdev/main.rb +68 -80
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 509a1fe96d989df0a53f8a9234129ccce89504521adb358cfcd837053db5f1cd
4
- data.tar.gz: 12180a752fd2683f30cbfe15c853f6979fedbb3151547b3d7aefcc842c0d564e
3
+ metadata.gz: 188423201d5b4fe029a2384edd1bba6384c35d9054f72db0ea02b02c7aec1c37
4
+ data.tar.gz: 6cac980901cb2ff633067fb600c1064c3136a48ccce9d50f708c54104efa5d4e
5
5
  SHA512:
6
- metadata.gz: 3925a9f8ecbeb0e7ba875bf3ce3d2501e4654fdb96a52a197928f8903d22d0daa2d8e1771f54d8c2c3060d3204a03e21f28cb0e50d8f536e386b607d2f7c94b9
7
- data.tar.gz: e091f4dda9ce752cf7f64b4f6464699189c9d3c380e995a63e390cf7f7b1223d9e6e9ea7143bce13c1236a01a364a7f0d0ce2b84cfa28a22b6c8483c83317618
6
+ metadata.gz: b4f77338f7f084b345df2538fdd555c8581d68ebb7ab2a80c4a37dece6aabf196094d1c7310cbc5e19d0ce6d8aadec44ee0150160961e71fb634eda30b3a98d2
7
+ data.tar.gz: 27ba6483e613dffdbaa2386661ae2533edcff4de5c296d4b223edc068103408dc042414c9fba1f92086fcb1f99a27b992ee0ce105c9c370163a71101dcd5c67a
data/bin/lxdev CHANGED
@@ -32,16 +32,15 @@ def option_parser
32
32
  Commands:
33
33
  up Bring container up
34
34
  status Show status of the container
35
- ssh Log into the container
35
+ ssh Log into the container
36
36
  halt Stop the container
37
- destroy Destroy the container
37
+ destroy Destroy the container
38
38
  provision Run provisioning commands from YAML file
39
39
  exec Run command as root in container
40
40
  snapshot Snapshots the container. Takes a snapshot name as parameter.
41
41
  rmsnapshot Deletes a snapshot. Takes a snapshot name as parameter.
42
42
  restore Restore container to a snapshot with a previous state. Takes a snapshot name as parameter.
43
43
  revert Restore container to the last snapshot taken.
44
- sudoers Create a sudoers file, allowing you to use lxdev without entering the sudo password
45
44
  EOS
46
45
  opt_parser.parse!()
47
46
 
@@ -74,8 +73,6 @@ def execute_main_command(lxdev)
74
73
  else
75
74
  lxdev.execute(command)
76
75
  end
77
- when 'sudoers'
78
- LxDev::Main.create_sudoers_file()
79
76
  when 'snapshot'
80
77
  snapshot_name = ARGV[1]
81
78
  if snapshot_name.nil?
data/lib/lxdev/main.rb CHANGED
@@ -3,15 +3,16 @@ require 'json'
3
3
  require 'terminal-table'
4
4
  require 'lxdev/system'
5
5
  require 'tempfile'
6
+ require 'etc'
6
7
 
7
8
  module LxDev
8
9
  class Main
9
- WHITELISTED_SUDO_COMMANDS = ["lxc", "redir", "kill"]
10
+ REQUIRED_COMMANDS = ["lxc", "redir", "kill"]
10
11
  SHELLS = ["bash", "zsh", "sh", "csh", "tcsh", "ash"]
11
12
  BOOT_TIMEOUT = 30
12
- VERSION = '0.1.8'
13
+ VERSION = '0.2.0'
13
14
 
14
- def initialize(config_file, state_file)
15
+ def initialize(config_file, state_file, lxc_command)
15
16
  @state_file = format(".lxdev/%s", state_file)
16
17
  @uid = System.exec("id -u").output.chomp
17
18
  @gid = System.exec("id -g").output.chomp
@@ -20,6 +21,7 @@ module LxDev
20
21
  @image = @config['box']['image']
21
22
  @user = @config['box']['user']
22
23
  @ports = @config['box']['ports'] || {}
24
+ @lxc_command = lxc_command
23
25
  Dir.mkdir('.lxdev') unless File.directory?('.lxdev')
24
26
  begin
25
27
  @state = YAML.load_file(@state_file)
@@ -37,7 +39,14 @@ module LxDev
37
39
  puts "Please run 'lxd init' and configure LXD first"
38
40
  return false
39
41
  end
40
- lxdev = Main.new(config_file, state_file)
42
+ user_in_group = Main.user_in_lxd_group?
43
+ lxc_command = (user_in_group ? "lxc" : "sudo lxc")
44
+ unless(user_in_group)
45
+ puts "Add yourself to the 'lxd' group to avoid entering the sudo password :"
46
+ puts "(You need to login again afterwards, or start a new login shell)"
47
+ puts " sudo usermod -a -G lxd #{Etc.getlogin}"
48
+ end
49
+ lxdev = Main.new(config_file, state_file, lxc_command)
41
50
  unless lxdev.set_ssh_keys
42
51
  puts "No ssh keys detected. Make sure you have an ssh key, a running agent, and the key added to the agent, e.g. with ssh-add."
43
52
  return false
@@ -45,6 +54,15 @@ module LxDev
45
54
  return lxdev
46
55
  end
47
56
 
57
+ def self.user_in_lxd_group?
58
+ begin
59
+ Etc.getgrnam("lxd").mem.include?(Etc.getlogin)
60
+ rescue ArgumentError
61
+ puts "There seems to be no 'lxd' group. Is LXD installed?"
62
+ false
63
+ end
64
+ end
65
+
48
66
  def save_state
49
67
  File.open(@state_file, 'w') {|f| f.write @state.to_yaml} unless @state.empty?
50
68
  end
@@ -114,14 +132,14 @@ module LxDev
114
132
 
115
133
  def halt
116
134
  ensure_container_created
117
- System.exec("sudo lxc stop #{@name}")
135
+ System.exec("#{@lxc_command} stop #{@name}")
118
136
  cleanup_forwarded_ports
119
137
  remove_state
120
138
  end
121
139
 
122
140
  def destroy
123
141
  ensure_container_created
124
- System.exec("sudo lxc delete #{@name}")
142
+ System.exec("#{@lxc_command} delete #{@name}")
125
143
  end
126
144
 
127
145
  def ssh(args)
@@ -137,9 +155,9 @@ module LxDev
137
155
 
138
156
  def execute(command, interactive: false)
139
157
  if interactive
140
- exec("sudo lxc exec #{@name} #{command}") # execution stops here and gives control to exec
158
+ exec("#{@lxc_command} exec #{@name} #{command}") # execution stops here and gives control to exec
141
159
  end
142
- IO.popen("sudo lxc exec #{@name} -- /bin/sh -c '#{command}'", err: [:child, :out]) do |cmd_output|
160
+ IO.popen("#{@lxc_command} exec #{@name} -- /bin/sh -c '#{command}'", err: [:child, :out]) do |cmd_output|
143
161
  cmd_output.each do |line|
144
162
  puts line
145
163
  end
@@ -171,18 +189,18 @@ module LxDev
171
189
 
172
190
  def snapshot(snapshot_name)
173
191
  puts "Creating snapshot #{snapshot_name}"
174
- System.exec("sudo lxc snapshot #{@name} #{snapshot_name}")
192
+ System.exec("#{@lxc_command} snapshot #{@name} #{snapshot_name}")
175
193
  end
176
194
 
177
195
  def restore(snapshot_name)
178
196
  puts "Restoring snapshot #{snapshot_name}"
179
- exitstatus = System.exec("sudo lxc restore #{@name} #{snapshot_name}").exitstatus
197
+ exitstatus = System.exec("#{@lxc_command} restore #{@name} #{snapshot_name}").exitstatus
180
198
  exitstatus == 0
181
199
  end
182
200
 
183
201
  def rmsnapshot(snapshot_name)
184
202
  puts "Deleting snapshot #{snapshot_name}"
185
- exitstatus = System.exec("sudo lxc delete #{@name}/#{snapshot_name}").exitstatus
203
+ exitstatus = System.exec("#{@lxc_command} delete #{@name}/#{snapshot_name}").exitstatus
186
204
  exitstatus == 0
187
205
  end
188
206
 
@@ -199,7 +217,7 @@ module LxDev
199
217
  private
200
218
 
201
219
  def self.lxd_initialized?
202
- exitstatus = System.exec("sudo lxc info | grep 'lxd init'").exitstatus
220
+ exitstatus = System.exec("#{@lxc_command} info | grep 'lxd init'").exitstatus
203
221
  exitstatus != 0
204
222
  end
205
223
 
@@ -219,10 +237,10 @@ module LxDev
219
237
  def create_container
220
238
  add_subuid_and_subgid
221
239
  puts "Launching #{@name}..."
222
- System.exec("sudo lxc init #{@image} #{@name}")
223
- System.exec(%{printf "uid #{@uid} 1001\ngid #{@gid} 1001"| sudo lxc config set #{@name} raw.idmap -})
224
- System.exec(%{sudo lxc config set #{@name} boot.autostart false})
225
- System.exec("sudo lxc start #{@name}")
240
+ System.exec("#{@lxc_command} init #{@image} #{@name}")
241
+ System.exec(%{printf "uid #{@uid} 1001\ngid #{@gid} 1001"| #{@lxc_command} config set #{@name} raw.idmap -})
242
+ System.exec(%{#{@lxc_command} config set #{@name} boot.autostart false})
243
+ System.exec("#{@lxc_command} start #{@name}")
226
244
  puts "Creating user #{@user}..."
227
245
  create_container_user(@user)
228
246
  puts "Mapping folders.."
@@ -231,12 +249,12 @@ module LxDev
231
249
 
232
250
  def start_container
233
251
  puts "Starting #{@name}..."
234
- System.exec("sudo lxc start #{@name}")
252
+ System.exec("#{@lxc_command} start #{@name}")
235
253
  end
236
254
 
237
255
  def get_container_status
238
256
  return @status unless @status.nil?
239
- command_result = System.exec("sudo lxc list ^#{@name}$ --format=json")
257
+ command_result = System.exec("#{@lxc_command} list ^#{@name}$ --format=json")
240
258
  @status = JSON.parse(command_result.output)
241
259
  end
242
260
 
@@ -266,16 +284,16 @@ module LxDev
266
284
  end
267
285
 
268
286
  def create_container_user(user)
269
- System.exec("sudo lxc exec #{@name} -- groupadd --gid 1001 #{user}")
270
- System.exec("sudo lxc exec #{@name} -- useradd --uid 1001 --gid 1001 -s /bin/bash -m #{user}")
271
- System.exec("sudo lxc exec #{@name} -- mkdir /home/#{user}/.ssh")
272
- System.exec("sudo lxc exec #{@name} -- chmod 0700 /home/#{user}/.ssh")
273
- System.exec("printf '#{@ssh_keys}' | sudo lxc exec #{@name} tee /home/#{user}/.ssh/authorized_keys")
274
- System.exec("sudo lxc exec #{@name} -- chown -R #{user} /home/#{user}/.ssh")
275
- System.exec("sudo lxc exec #{@name} -- touch /home/#{@user}/.hushlogin")
276
- System.exec("sudo lxc exec #{@name} -- chown #{user} /home/#{user}/.hushlogin")
277
- System.exec(%{printf "#{user} ALL=(ALL) NOPASSWD: ALL\n" | sudo lxc exec #{@name} -- tee -a /etc/sudoers})
278
- System.exec("sudo lxc exec #{@name} -- chmod 0440 /etc/sudoers")
287
+ System.exec("#{@lxc_command} exec #{@name} -- groupadd --gid 1001 #{user}")
288
+ System.exec("#{@lxc_command} exec #{@name} -- useradd --uid 1001 --gid 1001 -s /bin/bash -m #{user}")
289
+ System.exec("#{@lxc_command} exec #{@name} -- mkdir /home/#{user}/.ssh")
290
+ System.exec("#{@lxc_command} exec #{@name} -- chmod 0700 /home/#{user}/.ssh")
291
+ System.exec("printf '#{@ssh_keys}' | #{@lxc_command} exec #{@name} tee /home/#{user}/.ssh/authorized_keys")
292
+ System.exec("#{@lxc_command} exec #{@name} -- chown -R #{user} /home/#{user}/.ssh")
293
+ System.exec("#{@lxc_command} exec #{@name} -- touch /home/#{@user}/.hushlogin")
294
+ System.exec("#{@lxc_command} exec #{@name} -- chown #{user} /home/#{user}/.hushlogin")
295
+ System.exec(%{printf "#{user} ALL=(ALL) NOPASSWD: ALL\n" | #{@lxc_command} exec #{@name} -- tee -a /etc/sudoers})
296
+ System.exec("#{@lxc_command} exec #{@name} -- chmod 0440 /etc/sudoers")
279
297
  end
280
298
 
281
299
  def wait_for_boot
@@ -289,20 +307,33 @@ module LxDev
289
307
 
290
308
  def forward_ports(ports)
291
309
  redir_pids = []
310
+ sudo_redir_pids = []
292
311
  ports.each do |guest, host|
293
- puts "Forwarding #{get_container_ip}:#{guest} to local port #{host}"
294
- pid = System.spawn_exec("sudo redir --caddr=#{get_container_ip} --cport=#{guest} --lport=#{host}", silent: true)
295
- redir_pids << pid
296
- Process.detach(pid)
312
+ if (host > 1024)
313
+ puts "Forwarding #{get_container_ip}:#{guest} to local port #{host}"
314
+ pid = System.spawn_exec("redir --caddr=#{get_container_ip} --cport=#{guest} --lport=#{host}", silent: true)
315
+ redir_pids << pid
316
+ Process.detach(pid)
317
+ else
318
+ puts "Forwarding #{get_container_ip}:#{guest} to local port #{host} (root privileges needed)"
319
+ pid = System.spawn_exec("sudo redir --caddr=#{get_container_ip} --cport=#{guest} --lport=#{host}", silent: true)
320
+ sudo_redir_pids << pid
321
+ Process.detach(pid)
322
+ end
297
323
  end
298
324
  @state['redir_pids'] = redir_pids
325
+ @state['sudo_redir_pids'] = sudo_redir_pids
299
326
  end
300
327
 
301
328
  def cleanup_forwarded_ports
302
329
  if @state.empty?
303
330
  return
304
331
  end
305
- @state['redir_pids'].each do |pid|
332
+ @state['redir_pids']&.each do |pid|
333
+ System.exec("kill #{pid}")
334
+ end
335
+ @state['sudo_redir_pids']&.each do |pid|
336
+ puts "Killing pid #{pid} started with sudo privileges"
306
337
  System.exec("sudo kill #{pid}")
307
338
  end
308
339
  end
@@ -313,7 +344,7 @@ module LxDev
313
344
  counter = counter + 1
314
345
  puts "Mounting #{host} in #{guest}"
315
346
  absolute_path = System.exec("readlink -f #{host}").output.chomp
316
- System.exec("sudo lxc config device add #{@name} shared_folder_#{counter} disk source=#{absolute_path} path=#{guest}")
347
+ System.exec("#{@lxc_command} config device add #{@name} shared_folder_#{counter} disk source=#{absolute_path} path=#{guest}")
317
348
  end
318
349
  end
319
350
 
@@ -334,9 +365,9 @@ module LxDev
334
365
  end
335
366
 
336
367
  def lxd_service_name
337
- if System.exec("sudo systemctl status lxd.service").exitstatus == 0
368
+ if System.exec("systemctl status lxd.service").exitstatus == 0
338
369
  'lxd.service'
339
- elsif System.exec("sudo systemctl status snap.lxd.daemon.service").exitstatus == 0
370
+ elsif System.exec("systemctl status snap.lxd.daemon.service").exitstatus == 0
340
371
  'snap.lxd.daemon.service'
341
372
  else
342
373
  raise 'There seems to be no LXD service on the system!'
@@ -344,7 +375,7 @@ module LxDev
344
375
  end
345
376
 
346
377
  def self.check_requirements
347
- WHITELISTED_SUDO_COMMANDS.each do |cmd|
378
+ REQUIRED_COMMANDS.each do |cmd|
348
379
  unless System.exec("which #{cmd}").exitstatus == 0
349
380
  puts "The command '#{cmd}' is not installed or not available."
350
381
  puts "Please install it before continuing."
@@ -352,48 +383,5 @@ module LxDev
352
383
  end
353
384
  end
354
385
  end
355
-
356
- def self.create_sudoers_file
357
- self.check_requirements
358
- user = System.exec("whoami").output.chomp
359
- content = []
360
- content << "# Created by lxdev #{Time.now}"
361
- WHITELISTED_SUDO_COMMANDS.each do |cmd|
362
- cmd_with_path = System.exec("which #{cmd}").output.chomp
363
- content << "#{user} ALL=(root) NOPASSWD: #{cmd_with_path}"
364
- end
365
- content << "\n"
366
- puts <<-EOS
367
- !! WARNING !!
368
- This will create a file, /etc/sudoers.d/lxdev,
369
- which will give your user #{user} access to running
370
- the following commands :
371
- #{WHITELISTED_SUDO_COMMANDS.join(" ")}
372
- with superuser privileges. If you do not know what you're
373
- doing, this can be dangerous and insecure.
374
-
375
- EOS
376
- puts "The following content will be created in /etc/sudoers.d/lxdev :"
377
- puts
378
- puts content
379
- puts "\nIf you want to do this, type 'yesplease'"
380
- action = STDIN.gets.chomp
381
- unless action == 'yesplease'
382
- puts "Not creating sudoers file"
383
- return
384
- end
385
- temp_file = Tempfile.create('lxdev-sudoers')
386
- temp_file.write(content.join("\n"))
387
- temp_file_name = temp_file.to_path
388
- temp_file.close
389
- unless System.exec("visudo -c -f #{temp_file_name}").exitstatus == 0
390
- puts "Generated sudoers file contains errors, aborting."
391
- exit 1
392
- end
393
- System.exec("sudo chown root:root #{temp_file_name}")
394
- System.exec("sudo chmod 0440 #{temp_file_name}")
395
- System.exec("sudo mv #{temp_file_name} /etc/sudoers.d/lxdev")
396
- puts "Created sudoers file."
397
- end
398
386
  end
399
387
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lxdev
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Lønaas
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-03-03 00:00:00.000000000 Z
12
+ date: 2020-03-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json