leap_cli 1.2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/leap +81 -0
- data/lib/core_ext/boolean.rb +14 -0
- data/lib/core_ext/hash.rb +35 -0
- data/lib/core_ext/json.rb +42 -0
- data/lib/core_ext/nil.rb +5 -0
- data/lib/core_ext/string.rb +14 -0
- data/lib/leap/platform.rb +52 -0
- data/lib/leap_cli/commands/ca.rb +430 -0
- data/lib/leap_cli/commands/clean.rb +16 -0
- data/lib/leap_cli/commands/compile.rb +134 -0
- data/lib/leap_cli/commands/deploy.rb +172 -0
- data/lib/leap_cli/commands/facts.rb +93 -0
- data/lib/leap_cli/commands/inspect.rb +140 -0
- data/lib/leap_cli/commands/list.rb +122 -0
- data/lib/leap_cli/commands/new.rb +126 -0
- data/lib/leap_cli/commands/node.rb +272 -0
- data/lib/leap_cli/commands/pre.rb +99 -0
- data/lib/leap_cli/commands/shell.rb +67 -0
- data/lib/leap_cli/commands/test.rb +55 -0
- data/lib/leap_cli/commands/user.rb +140 -0
- data/lib/leap_cli/commands/util.rb +50 -0
- data/lib/leap_cli/commands/vagrant.rb +201 -0
- data/lib/leap_cli/config/macros.rb +369 -0
- data/lib/leap_cli/config/manager.rb +369 -0
- data/lib/leap_cli/config/node.rb +37 -0
- data/lib/leap_cli/config/object.rb +336 -0
- data/lib/leap_cli/config/object_list.rb +174 -0
- data/lib/leap_cli/config/secrets.rb +43 -0
- data/lib/leap_cli/config/tag.rb +18 -0
- data/lib/leap_cli/constants.rb +7 -0
- data/lib/leap_cli/leapfile.rb +97 -0
- data/lib/leap_cli/load_paths.rb +15 -0
- data/lib/leap_cli/log.rb +166 -0
- data/lib/leap_cli/logger.rb +216 -0
- data/lib/leap_cli/markdown_document_listener.rb +134 -0
- data/lib/leap_cli/path.rb +84 -0
- data/lib/leap_cli/remote/leap_plugin.rb +204 -0
- data/lib/leap_cli/remote/puppet_plugin.rb +66 -0
- data/lib/leap_cli/remote/rsync_plugin.rb +35 -0
- data/lib/leap_cli/remote/tasks.rb +36 -0
- data/lib/leap_cli/requirements.rb +19 -0
- data/lib/leap_cli/ssh_key.rb +130 -0
- data/lib/leap_cli/util/remote_command.rb +110 -0
- data/lib/leap_cli/util/secret.rb +54 -0
- data/lib/leap_cli/util/x509.rb +32 -0
- data/lib/leap_cli/util.rb +431 -0
- data/lib/leap_cli/version.rb +9 -0
- data/lib/leap_cli.rb +46 -0
- data/lib/lib_ext/capistrano_connections.rb +16 -0
- data/lib/lib_ext/gli.rb +52 -0
- data/lib/lib_ext/markdown_document_listener.rb +122 -0
- data/vendor/certificate_authority/lib/certificate_authority/certificate.rb +200 -0
- data/vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb +77 -0
- data/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb +97 -0
- data/vendor/certificate_authority/lib/certificate_authority/extensions.rb +266 -0
- data/vendor/certificate_authority/lib/certificate_authority/key_material.rb +148 -0
- data/vendor/certificate_authority/lib/certificate_authority/ocsp_handler.rb +144 -0
- data/vendor/certificate_authority/lib/certificate_authority/pkcs11_key_material.rb +65 -0
- data/vendor/certificate_authority/lib/certificate_authority/revocable.rb +14 -0
- data/vendor/certificate_authority/lib/certificate_authority/serial_number.rb +10 -0
- data/vendor/certificate_authority/lib/certificate_authority/signing_entity.rb +16 -0
- data/vendor/certificate_authority/lib/certificate_authority/signing_request.rb +56 -0
- data/vendor/certificate_authority/lib/certificate_authority.rb +21 -0
- data/vendor/rsync_command/lib/rsync_command/ssh_options.rb +159 -0
- data/vendor/rsync_command/lib/rsync_command/thread_pool.rb +36 -0
- data/vendor/rsync_command/lib/rsync_command/version.rb +3 -0
- data/vendor/rsync_command/lib/rsync_command.rb +96 -0
- data/vendor/rsync_command/test/rsync_test.rb +74 -0
- data/vendor/rsync_command/test/ssh_options_test.rb +61 -0
- data/vendor/vagrant_ssh_keys/vagrant.key +27 -0
- data/vendor/vagrant_ssh_keys/vagrant.pub +1 -0
- metadata +345 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'gpgme'
|
2
|
+
|
3
|
+
#
|
4
|
+
# perhaps we want to verify that the key files are actually the key files we expect.
|
5
|
+
# we could use 'file' for this:
|
6
|
+
#
|
7
|
+
# > file ~/.gnupg/00440025.asc
|
8
|
+
# ~/.gnupg/00440025.asc: PGP public key block
|
9
|
+
#
|
10
|
+
# > file ~/.ssh/id_rsa.pub
|
11
|
+
# ~/.ssh/id_rsa.pub: OpenSSH RSA public key
|
12
|
+
#
|
13
|
+
|
14
|
+
module LeapCli
|
15
|
+
module Commands
|
16
|
+
|
17
|
+
desc 'Adds a new trusted sysadmin by adding public keys to the "users" directory.'
|
18
|
+
arg_name 'USERNAME' #, :optional => false, :multiple => false
|
19
|
+
command :'add-user' do |c|
|
20
|
+
|
21
|
+
c.switch 'self', :desc => 'Add yourself as a trusted sysadin by choosing among the public keys available for the current user.', :negatable => false
|
22
|
+
c.flag 'ssh-pub-key', :desc => 'SSH public key file for this new user'
|
23
|
+
c.flag 'pgp-pub-key', :desc => 'OpenPGP public key file for this new user'
|
24
|
+
|
25
|
+
c.action do |global_options,options,args|
|
26
|
+
username = args.first
|
27
|
+
if !username.any? && !options[:self]
|
28
|
+
help! "Either 'username' or --self is required."
|
29
|
+
end
|
30
|
+
|
31
|
+
ssh_pub_key = nil
|
32
|
+
pgp_pub_key = nil
|
33
|
+
|
34
|
+
if options['ssh-pub-key']
|
35
|
+
ssh_pub_key = read_file!(options['ssh-pub-key'])
|
36
|
+
end
|
37
|
+
if options['pgp-pub-key']
|
38
|
+
pgp_pub_key = read_file!(options['pgp-pub-key'])
|
39
|
+
end
|
40
|
+
|
41
|
+
if options[:self]
|
42
|
+
username ||= `whoami`.strip
|
43
|
+
ssh_pub_key ||= pick_ssh_key.to_s
|
44
|
+
pgp_pub_key ||= pick_pgp_key
|
45
|
+
end
|
46
|
+
|
47
|
+
assert!(ssh_pub_key, 'Sorry, could not find SSH public key.')
|
48
|
+
|
49
|
+
if ssh_pub_key
|
50
|
+
write_file!([:user_ssh, username], ssh_pub_key)
|
51
|
+
end
|
52
|
+
if pgp_pub_key
|
53
|
+
write_file!([:user_pgp, username], pgp_pub_key)
|
54
|
+
end
|
55
|
+
|
56
|
+
update_authorized_keys
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# let the the user choose among the ssh public keys that we encounter, or just pick the key if there is only one.
|
62
|
+
#
|
63
|
+
def pick_ssh_key
|
64
|
+
ssh_keys = []
|
65
|
+
Dir.glob("#{ENV['HOME']}/.ssh/*.pub").each do |keyfile|
|
66
|
+
ssh_keys << SshKey.load(keyfile)
|
67
|
+
end
|
68
|
+
|
69
|
+
if `which ssh-add`.strip.any?
|
70
|
+
`ssh-add -L 2> /dev/null`.split("\n").compact.each do |line|
|
71
|
+
key = SshKey.load(line)
|
72
|
+
key.comment = 'ssh-agent'
|
73
|
+
ssh_keys << key unless ssh_keys.include?(key)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
ssh_keys.compact!
|
77
|
+
|
78
|
+
assert! ssh_keys.any?, 'Sorry, could not find any SSH public key for you. Have you run ssh-keygen?'
|
79
|
+
|
80
|
+
if ssh_keys.length > 1
|
81
|
+
key_index = numbered_choice_menu('Choose your SSH public key', ssh_keys.collect(&:summary)) do |line, i|
|
82
|
+
say("#{i+1}. #{line}")
|
83
|
+
end
|
84
|
+
else
|
85
|
+
key_index = 0
|
86
|
+
end
|
87
|
+
|
88
|
+
return ssh_keys[key_index]
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# let the the user choose among the gpg public keys that we encounter, or just pick the key if there is only one.
|
93
|
+
#
|
94
|
+
def pick_pgp_key
|
95
|
+
secret_keys = GPGME::Key.find(:secret)
|
96
|
+
if secret_keys.empty?
|
97
|
+
log "Skipping OpenPGP setup because I could not find any OpenPGP keys for you"
|
98
|
+
return nil
|
99
|
+
end
|
100
|
+
|
101
|
+
assert_bin! 'gpg'
|
102
|
+
|
103
|
+
if secret_keys.length > 1
|
104
|
+
key_index = numbered_choice_menu('Choose your OpenPGP public key', secret_keys) do |key, i|
|
105
|
+
key_info = key.to_s.split("\n")[0..1].map{|line| line.sub(/^\s*(sec|uid)\s*/,'')}.join(' -- ')
|
106
|
+
say("#{i+1}. #{key_info}")
|
107
|
+
end
|
108
|
+
else
|
109
|
+
key_index = 0
|
110
|
+
end
|
111
|
+
|
112
|
+
key_id = secret_keys[key_index].sha
|
113
|
+
|
114
|
+
# can't use this, it includes signatures:
|
115
|
+
#puts GPGME::Key.export(key_id, :armor => true, :export_options => :export_minimal)
|
116
|
+
|
117
|
+
# export with signatures removed:
|
118
|
+
return `gpg --armor --export-options export-minimal --export #{key_id}`.strip
|
119
|
+
end
|
120
|
+
|
121
|
+
def update_authorized_keys
|
122
|
+
buffer = StringIO.new
|
123
|
+
keys = Dir.glob(path([:user_ssh, '*']))
|
124
|
+
if keys.empty?
|
125
|
+
bail! "You must have at least one public SSH user key configured in order to proceed. See `leap help add-user`."
|
126
|
+
end
|
127
|
+
keys.sort.each do |keyfile|
|
128
|
+
ssh_type, ssh_key = File.read(keyfile).strip.split(" ")
|
129
|
+
buffer << ssh_type
|
130
|
+
buffer << " "
|
131
|
+
buffer << ssh_key
|
132
|
+
buffer << " "
|
133
|
+
buffer << Path.relative_path(keyfile)
|
134
|
+
buffer << "\n"
|
135
|
+
end
|
136
|
+
write_file!(:authorized_keys, buffer.string)
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module LeapCli; module Commands
|
2
|
+
|
3
|
+
extend self
|
4
|
+
extend LeapCli::Util
|
5
|
+
extend LeapCli::Util::RemoteCommand
|
6
|
+
|
7
|
+
def path(name)
|
8
|
+
Path.named_path(name)
|
9
|
+
end
|
10
|
+
|
11
|
+
#
|
12
|
+
# keeps prompting the user for a numbered choice, until they pick a good one or bail out.
|
13
|
+
#
|
14
|
+
# block is yielded and is responsible for rendering the choices.
|
15
|
+
#
|
16
|
+
def numbered_choice_menu(msg, items, &block)
|
17
|
+
while true
|
18
|
+
say("\n" + msg + ':')
|
19
|
+
items.each_with_index &block
|
20
|
+
say("q. quit")
|
21
|
+
index = ask("number 1-#{items.length}> ")
|
22
|
+
if index.empty?
|
23
|
+
next
|
24
|
+
elsif index =~ /q/
|
25
|
+
bail!
|
26
|
+
else
|
27
|
+
i = index.to_i - 1
|
28
|
+
if i < 0 || i >= items.length
|
29
|
+
bail!
|
30
|
+
else
|
31
|
+
return i
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def parse_node_list(nodes)
|
39
|
+
if nodes.is_a? Config::Object
|
40
|
+
Config::ObjectList.new(nodes)
|
41
|
+
elsif nodes.is_a? Config::ObjectList
|
42
|
+
nodes
|
43
|
+
elsif nodes.is_a? String
|
44
|
+
manager.filter!(nodes)
|
45
|
+
else
|
46
|
+
bail! "argument error"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end; end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module LeapCli; module Commands
|
5
|
+
|
6
|
+
desc "Manage local virtual machines."
|
7
|
+
long_desc "This command provides a convient way to manage Vagrant-based virtual machines. If FILTER argument is missing, the command runs on all local virtual machines. The Vagrantfile is automatically generated in 'test/Vagrantfile'. If you want to run vagrant commands manually, cd to 'test'."
|
8
|
+
command :local do |local|
|
9
|
+
local.desc 'Starts up the virtual machine(s)'
|
10
|
+
local.arg_name 'FILTER', :optional => true #, :multiple => false
|
11
|
+
local.command :start do |start|
|
12
|
+
start.action do |global_options,options,args|
|
13
|
+
vagrant_command(["up", "sandbox on"], args)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
local.desc 'Shuts down the virtual machine(s)'
|
18
|
+
local.arg_name 'FILTER', :optional => true #, :multiple => false
|
19
|
+
local.command :stop do |stop|
|
20
|
+
stop.action do |global_options,options,args|
|
21
|
+
if global_options[:yes]
|
22
|
+
vagrant_command("halt --force", args)
|
23
|
+
else
|
24
|
+
vagrant_command("halt", args)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
local.desc 'Destroys the virtual machine(s), reclaiming the disk space'
|
30
|
+
local.arg_name 'FILTER', :optional => true #, :multiple => false
|
31
|
+
local.command :destroy do |destroy|
|
32
|
+
destroy.action do |global_options,options,args|
|
33
|
+
if global_options[:yes]
|
34
|
+
vagrant_command("destroy --force", args)
|
35
|
+
else
|
36
|
+
vagrant_command("destroy", args)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
local.desc 'Print the status of local virtual machine(s)'
|
42
|
+
local.arg_name 'FILTER', :optional => true #, :multiple => false
|
43
|
+
local.command :status do |status|
|
44
|
+
status.action do |global_options,options,args|
|
45
|
+
vagrant_command("status", args)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
local.desc 'Saves the current state of the virtual machine as a new snapshot'
|
50
|
+
local.arg_name 'FILTER', :optional => true #, :multiple => false
|
51
|
+
local.command :save do |status|
|
52
|
+
status.action do |global_options,options,args|
|
53
|
+
vagrant_command("sandbox commit", args)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
local.desc 'Resets virtual machine(s) to the last saved snapshot'
|
58
|
+
local.arg_name 'FILTER', :optional => true #, :multiple => false
|
59
|
+
local.command :reset do |reset|
|
60
|
+
reset.action do |global_options,options,args|
|
61
|
+
vagrant_command("sandbox rollback", args)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
public
|
67
|
+
|
68
|
+
#
|
69
|
+
# returns the path to a vagrant ssh key file.
|
70
|
+
#
|
71
|
+
# if the vagrant.key file is owned by root or ourselves, then
|
72
|
+
# we need to make sure that it owned by us and not world readable.
|
73
|
+
#
|
74
|
+
def vagrant_ssh_key_file
|
75
|
+
file_path = File.expand_path('../../../vendor/vagrant_ssh_keys/vagrant.key', File.dirname(__FILE__))
|
76
|
+
Util.assert_files_exist! file_path
|
77
|
+
uid = File.new(file_path).stat.uid
|
78
|
+
if uid == 0 || uid == Process.euid
|
79
|
+
FileUtils.install file_path, '/tmp/vagrant.key', :mode => 0600
|
80
|
+
file_path = '/tmp/vagrant.key'
|
81
|
+
end
|
82
|
+
return file_path
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def vagrant_command(cmds, args)
|
88
|
+
vagrant_setup
|
89
|
+
cmds = cmds.to_a
|
90
|
+
if args.empty?
|
91
|
+
nodes = [""]
|
92
|
+
else
|
93
|
+
nodes = manager.filter(args)[:environment => "local"].field(:name)
|
94
|
+
end
|
95
|
+
if nodes.any?
|
96
|
+
vagrant_dir = File.dirname(Path.named_path(:vagrantfile))
|
97
|
+
exec = ["cd #{vagrant_dir}"]
|
98
|
+
cmds.each do |cmd|
|
99
|
+
nodes.each do |node|
|
100
|
+
exec << "vagrant #{cmd} #{node}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
execute exec.join('; ')
|
104
|
+
else
|
105
|
+
bail! "No nodes found. This command only works on nodes with ip_address in the network #{LeapCli.leapfile.vagrant_network}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def vagrant_setup
|
112
|
+
assert_bin! 'vagrant', 'Vagrant is required for running local virtual machines. Run "sudo apt-get install vagrant".'
|
113
|
+
|
114
|
+
version = vagrant_version
|
115
|
+
case version
|
116
|
+
when 0..1
|
117
|
+
gem_path = assert_run!('vagrant gem which sahara')
|
118
|
+
if gem_path.nil? || gem_path.empty? || gem_path =~ /^ERROR/
|
119
|
+
log :installing, "vagrant plugin 'sahara'"
|
120
|
+
assert_run! 'vagrant gem install sahara -v 0.0.13'
|
121
|
+
# (sahara versions above 0.0.13 require vagrant > 1.0)
|
122
|
+
end
|
123
|
+
when 2
|
124
|
+
unless assert_run!('vagrant plugin list | grep sahara | cat').chars.any?
|
125
|
+
log :installing, "vagrant plugin 'sahara'"
|
126
|
+
assert_run! 'vagrant plugin install sahara'
|
127
|
+
end
|
128
|
+
end
|
129
|
+
create_vagrant_file
|
130
|
+
end
|
131
|
+
|
132
|
+
def vagrant_version
|
133
|
+
minor_version = `vagrant --version | rev | cut -d'.' -f 2`.to_i
|
134
|
+
version = case minor_version
|
135
|
+
when 1..9 then 2
|
136
|
+
when 0 then 1
|
137
|
+
else 0
|
138
|
+
end
|
139
|
+
return version
|
140
|
+
end
|
141
|
+
|
142
|
+
def execute(cmd)
|
143
|
+
log 2, :run, cmd
|
144
|
+
exec cmd
|
145
|
+
end
|
146
|
+
|
147
|
+
def create_vagrant_file
|
148
|
+
lines = []
|
149
|
+
netmask = IPAddr.new('255.255.255.255').mask(LeapCli.leapfile.vagrant_network.split('/').last).to_s
|
150
|
+
|
151
|
+
version = vagrant_version
|
152
|
+
case version
|
153
|
+
when 0..1
|
154
|
+
lines << %[Vagrant::Config.run do |config|]
|
155
|
+
manager.each_node do |node|
|
156
|
+
if node.vagrant?
|
157
|
+
lines << %[ config.vm.define :#{node.name} do |config|]
|
158
|
+
lines << %[ config.vm.box = "leap-wheezy"]
|
159
|
+
lines << %[ config.vm.box_url = "http://download.leap.se/leap-debian.box"]
|
160
|
+
lines << %[ config.vm.network :hostonly, "#{node.ip_address}", :netmask => "#{netmask}"]
|
161
|
+
lines << %[ config.vm.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]]
|
162
|
+
lines << %[ config.vm.customize ["modifyvm", :id, "--name", "#{node.name}"]]
|
163
|
+
lines << %[ #{leapfile.custom_vagrant_vm_line}] if leapfile.custom_vagrant_vm_line
|
164
|
+
lines << %[ end]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
when 2
|
168
|
+
lines << %[Vagrant.configure("2") do |config|]
|
169
|
+
manager.each_node do |node|
|
170
|
+
if node.vagrant?
|
171
|
+
lines << %[ config.vm.define :#{node.name} do |config|]
|
172
|
+
lines << %[ config.vm.box = "leap-wheezy"]
|
173
|
+
lines << %[ config.vm.box_url = "http://download.leap.se/leap-debian.box"]
|
174
|
+
lines << %[ config.vm.network :private_network, ip: "#{node.ip_address}"]
|
175
|
+
lines << %[ config.vm.provider "virtualbox" do |v|]
|
176
|
+
lines << %[ v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]]
|
177
|
+
lines << %[ v.name = "#{node.name}"]
|
178
|
+
lines << %[ end]
|
179
|
+
lines << %[ #{leapfile.custom_vagrant_vm_line}] if leapfile.custom_vagrant_vm_line
|
180
|
+
lines << %[ end]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
lines << %[end]
|
186
|
+
lines << ""
|
187
|
+
write_file! :vagrantfile, lines.join("\n")
|
188
|
+
end
|
189
|
+
|
190
|
+
def pick_next_vagrant_ip_address
|
191
|
+
taken_ips = manager.nodes[:environment => "local"].field(:ip_address)
|
192
|
+
if taken_ips.any?
|
193
|
+
highest_ip = taken_ips.map{|ip| IPAddr.new(ip)}.max
|
194
|
+
new_ip = highest_ip.succ
|
195
|
+
else
|
196
|
+
new_ip = IPAddr.new(LeapCli.leapfile.vagrant_network).succ.succ
|
197
|
+
end
|
198
|
+
return new_ip.to_s
|
199
|
+
end
|
200
|
+
|
201
|
+
end; end
|