arver 0.0.5
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.
- data/CHANGELOG.textile +9 -0
- data/README.textile +120 -0
- data/bin/arver +10 -0
- data/lib/arver.rb +6 -0
- data/lib/arver/action.rb +78 -0
- data/lib/arver/adduser_action.rb +54 -0
- data/lib/arver/bootstrap.rb +35 -0
- data/lib/arver/cli.rb +133 -0
- data/lib/arver/close_action.rb +61 -0
- data/lib/arver/command_wrapper.rb +42 -0
- data/lib/arver/config.rb +54 -0
- data/lib/arver/create_action.rb +58 -0
- data/lib/arver/deluser_action.rb +34 -0
- data/lib/arver/gc_action.rb +12 -0
- data/lib/arver/gpg_key_manager.rb +80 -0
- data/lib/arver/host.rb +74 -0
- data/lib/arver/hostgroup.rb +17 -0
- data/lib/arver/info_action.rb +22 -0
- data/lib/arver/io_logger.rb +34 -0
- data/lib/arver/key_generator.rb +29 -0
- data/lib/arver/key_info_action.rb +29 -0
- data/lib/arver/key_saver.rb +128 -0
- data/lib/arver/keystore.rb +70 -0
- data/lib/arver/list_action.rb +7 -0
- data/lib/arver/local_config.rb +41 -0
- data/lib/arver/log.rb +39 -0
- data/lib/arver/log_levels.rb +9 -0
- data/lib/arver/luks_wrapper.rb +29 -0
- data/lib/arver/node_with_script_hooks.rb +25 -0
- data/lib/arver/open_action.rb +70 -0
- data/lib/arver/partition.rb +53 -0
- data/lib/arver/partition_hierarchy_node.rb +112 -0
- data/lib/arver/runtime_config.rb +22 -0
- data/lib/arver/ssh_command_wrapper.rb +21 -0
- data/lib/arver/string.rb +8 -0
- data/lib/arver/target_list.rb +33 -0
- data/lib/arver/test_config_loader.rb +21 -0
- data/lib/arver/test_partition.rb +9 -0
- data/lib/arver/tree.rb +32 -0
- data/lib/arver/version.rb +3 -0
- data/man/arver.5 +429 -0
- metadata +155 -0
data/CHANGELOG.textile
ADDED
data/README.textile
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
h1. arver
|
2
|
+
|
3
|
+
* https://git.codecoop.org/projects/arver
|
4
|
+
|
5
|
+
h2. DESCRIPTION:
|
6
|
+
|
7
|
+
arver is a tool to manage encrypted harddisks.
|
8
|
+
|
9
|
+
Imagine you are a collective with several admin members. Your servers have
|
10
|
+
diffrent LUKS encrypted devices.
|
11
|
+
|
12
|
+
Either you would need 1 password for every device which everyone needs to know
|
13
|
+
or you use arver! Arver has 1 password for each device and for each member. This
|
14
|
+
password is stored encrypted with the personal gpg-key in the data directory.
|
15
|
+
The admins only need to know the password to the their own gpg secret key.
|
16
|
+
|
17
|
+
This has the following advantages:
|
18
|
+
|
19
|
+
* No need to share passwords or password patterns
|
20
|
+
** Often people share passwords amongst each another. This has the drawback that
|
21
|
+
in case of an emergency every password needs to be changed. Which means that
|
22
|
+
everyone else needs to learn a bunch of new passwords and changing these
|
23
|
+
passwords is also quite cumbersome and time consuming.
|
24
|
+
** As the amount of passwords might grow with your disks and hosts you will start
|
25
|
+
using a password pattern to derive passwords for each disk from that pattern.
|
26
|
+
This has the drawback that you can hardly share only partial access to disks
|
27
|
+
with a certain admin, as if she knows the pattern she will also likely have
|
28
|
+
access to every other disk. Furthermore, if once one password is leaked and
|
29
|
+
the patter is easily visible, all the other passwords are also compromised.
|
30
|
+
* Managing your encrypted harddisks is scriptable, which means that you can
|
31
|
+
recover much faster from outages
|
32
|
+
* Revoking access for an admin is scriptable and therefore done in one call and
|
33
|
+
also much safer than revoking manually for each disk.
|
34
|
+
* Finer grained access. As for each user and each disk there will be a seperate
|
35
|
+
password by design. You can also grant access to certain disks also only
|
36
|
+
selectively. So for example new admins in your group can only open the disks
|
37
|
+
for your most important services or for which they are respnsible, while access
|
38
|
+
to the other disks is restriced to other admins.
|
39
|
+
|
40
|
+
h1. Usage
|
41
|
+
|
42
|
+
arver ships with a detailed man page, describing the usage in detail.
|
43
|
+
|
44
|
+
h1. Limitations
|
45
|
+
|
46
|
+
* arver supports only up to 8 users as LUKS has only 8 key slots (LUKS NUMKEYS).
|
47
|
+
|
48
|
+
h1. Known Issues
|
49
|
+
|
50
|
+
h2. GPGME and gpg-agent
|
51
|
+
|
52
|
+
If arver asks you multiple times for the password, you might consider to use
|
53
|
+
gpg-agent, so you can decrypt your keypair once and the use it for all your
|
54
|
+
stored keys.
|
55
|
+
|
56
|
+
You can test gpg-agent by trying to decrypt an encrypted file for your user in
|
57
|
+
data/keys/USERNAME/key_X . It will tell you about possible gpg-erorrs.
|
58
|
+
|
59
|
+
Configuring gpg-agent is quite simple and you find information on the following
|
60
|
+
website: http://dougbarton.us/PGP/gpg-agent.html
|
61
|
+
|
62
|
+
If you install gpg-agent like dougbarton recomends, you need to further verify
|
63
|
+
that the environment variable GPG_AGENT_INFO is accessible within the arver
|
64
|
+
script. An option is to add the following entry to your .bashrc
|
65
|
+
|
66
|
+
if [ -r "${HOME}/.gpg-agent-info" ]; then
|
67
|
+
. ${HOME}/.gpg-agent-info
|
68
|
+
export GPG_AGENT_INFO
|
69
|
+
fi
|
70
|
+
|
71
|
+
h1. Requirements
|
72
|
+
|
73
|
+
arver only works with cryptsetup-luks >= 1.0.5 as previous versions do not
|
74
|
+
support key slots properly for our usage.
|
75
|
+
|
76
|
+
h1. Installation
|
77
|
+
|
78
|
+
The easiest way to install arver is by gem
|
79
|
+
|
80
|
+
sudo gem install arver
|
81
|
+
|
82
|
+
This will install all required dependecies automatically. If your distributions
|
83
|
+
contains an arver package we recommend installation by your package manager.
|
84
|
+
|
85
|
+
The following ruby gems are required for arver:
|
86
|
+
|
87
|
+
* gpgme 2
|
88
|
+
* activesupport 2
|
89
|
+
* escape
|
90
|
+
|
91
|
+
For development you will need the following additional gems:
|
92
|
+
|
93
|
+
* rake
|
94
|
+
* cucumber
|
95
|
+
* rspec
|
96
|
+
|
97
|
+
h1. License
|
98
|
+
|
99
|
+
(The MIT License)
|
100
|
+
|
101
|
+
Copyright (c) 2010 arver
|
102
|
+
|
103
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
104
|
+
a copy of this software and associated documentation files (the
|
105
|
+
'Software'), to deal in the Software without restriction, including
|
106
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
107
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
108
|
+
permit persons to whom the Software is furnished to do so, subject to
|
109
|
+
the following conditions:
|
110
|
+
|
111
|
+
The above copyright notice and this permission notice shall be
|
112
|
+
included in all copies or substantial portions of the Software.
|
113
|
+
|
114
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
115
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
116
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
117
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
118
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
119
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
120
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/bin/arver
ADDED
data/lib/arver.rb
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
%w{ singleton yaml fileutils active_support gpgme escape openssl}.each {|f| require f }
|
2
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
3
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
4
|
+
|
5
|
+
%w{ gpg_key_manager luks_wrapper action create_action list_action gc_action adduser_action deluser_action info_action close_action open_action target_list command_wrapper ssh_command_wrapper log_levels io_logger log string bootstrap local_config config test_config_loader node_with_script_hooks partition_hierarchy_node host hostgroup tree partition test_partition key_generator key_saver keystore runtime_config key_info_action}.each {|f| require "arver/#{f}" }
|
6
|
+
|
data/lib/arver/action.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
module Arver
|
2
|
+
class Action
|
3
|
+
|
4
|
+
attr_accessor :keystore, :target_list, :target_user, :slot_of_target_user, :generator, :key
|
5
|
+
|
6
|
+
def initialize( target_list )
|
7
|
+
self.target_list= target_list
|
8
|
+
end
|
9
|
+
|
10
|
+
def on_user( username )
|
11
|
+
return true unless needs_target_user?
|
12
|
+
unless Arver::Config.instance.exists?(username)
|
13
|
+
Arver::Log.error( "No such user" )
|
14
|
+
return false
|
15
|
+
end
|
16
|
+
return false unless verify_key_on_target( username )
|
17
|
+
self.slot_of_target_user= Arver::Config.instance.slot( username )
|
18
|
+
self.target_user= username
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def verify_key_on_target( username )
|
23
|
+
Arver::GPGKeyManager.check_key_of( username )
|
24
|
+
end
|
25
|
+
|
26
|
+
def needs_target_user?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
def new_key_generator
|
31
|
+
self.generator= Arver::KeyGenerator.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def open_keystore
|
35
|
+
self.keystore= Arver::Keystore.instance
|
36
|
+
keystore.load
|
37
|
+
end
|
38
|
+
|
39
|
+
def run_on( node )
|
40
|
+
node.run_action( self ) if node.target?( self.target_list )
|
41
|
+
end
|
42
|
+
|
43
|
+
def pre_action
|
44
|
+
end
|
45
|
+
|
46
|
+
def post_action
|
47
|
+
end
|
48
|
+
|
49
|
+
def pre_host( host )
|
50
|
+
end
|
51
|
+
|
52
|
+
def verify?( partition )
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def load_key( partition )
|
57
|
+
self.key= keystore.luks_key( partition )
|
58
|
+
|
59
|
+
if( key.nil? )
|
60
|
+
Arver::Log.error( "No permission on #{partition.path}. Skipping." )
|
61
|
+
return false
|
62
|
+
end
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
def pre_partition( partition )
|
67
|
+
end
|
68
|
+
|
69
|
+
def execute_partition( partition )
|
70
|
+
end
|
71
|
+
|
72
|
+
def post_partition( partition )
|
73
|
+
end
|
74
|
+
|
75
|
+
def post_host( host )
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Arver
|
2
|
+
class AdduserAction < Action
|
3
|
+
|
4
|
+
def initialize( target_list )
|
5
|
+
super( target_list )
|
6
|
+
self.open_keystore
|
7
|
+
self.new_key_generator
|
8
|
+
end
|
9
|
+
|
10
|
+
def pre_action
|
11
|
+
tl = ""
|
12
|
+
target_list.each { |t| tl += ( tl.empty? ? "": ", " )+t.name }
|
13
|
+
Arver::Log.info( "adduser was called with target(s) #{tl} and user #{target_user} (slot-no #{slot_of_target_user})" )
|
14
|
+
end
|
15
|
+
|
16
|
+
def needs_target_user?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def verify?( partition )
|
21
|
+
if not Arver::RuntimeConfig.instance.ask_password then
|
22
|
+
return false unless load_key( partition )
|
23
|
+
else
|
24
|
+
self.key= ask("Enter the password for the volume: #{partition.device}") {|q| q.echo = false}
|
25
|
+
end
|
26
|
+
unless( Arver::LuksWrapper.open?(partition).execute )
|
27
|
+
Arver::Log.error( "WARNING: "+partition.name+" is not open. skipping." )
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def execute_partition( partition )
|
34
|
+
Arver::Log.info( "Generating keys for partition #{partition.device}" )
|
35
|
+
|
36
|
+
# generate a key for the new user
|
37
|
+
Arver::Log.debug( "generate_key (#{target_user},#{partition.path})" )
|
38
|
+
|
39
|
+
newkey = generator.generate_key( target_user, partition )
|
40
|
+
|
41
|
+
caller = Arver::LuksWrapper.addKey( slot_of_target_user.to_s, partition )
|
42
|
+
caller.execute( key + "\n" + newkey )
|
43
|
+
|
44
|
+
unless( caller.success? )
|
45
|
+
Arver::Log.error( "Could not add user to #{partition.path} \n #{caller.output}" )
|
46
|
+
generator.remove_key( target_user, partition )
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def post_action
|
51
|
+
self.generator.dump
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Arver::Bootstrap
|
2
|
+
class << self
|
3
|
+
def run(options)
|
4
|
+
local = Arver::LocalConfig.instance
|
5
|
+
local.config_dir = options[:config_dir] unless options[:config_dir].empty?
|
6
|
+
local.username = options[:user] unless options[:user].empty?
|
7
|
+
|
8
|
+
unless local.username.present?
|
9
|
+
Arver::Log.error( "No user defined" )
|
10
|
+
return false
|
11
|
+
end
|
12
|
+
|
13
|
+
config = Arver::Config.instance
|
14
|
+
config.load
|
15
|
+
|
16
|
+
self.load_runtime_config(options)
|
17
|
+
|
18
|
+
unless Arver::Config.instance.exists?(local.username)
|
19
|
+
Arver::Log.error( "No such user #{local.username}" )
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
Arver::GPGKeyManager.check_key_of(local.username)
|
23
|
+
end
|
24
|
+
|
25
|
+
def load_runtime_config(options)
|
26
|
+
rtc = Arver::RuntimeConfig.instance
|
27
|
+
rtc.dry_run = options[:dry_run]
|
28
|
+
rtc.ask_password = options[:ask_password]
|
29
|
+
rtc.force = options[:force]
|
30
|
+
rtc.violence = options[:violence]
|
31
|
+
rtc.test_mode = options[:test_mode]
|
32
|
+
rtc.trust_all = options[:trust_all]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/arver/cli.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Arver
|
4
|
+
class CLI
|
5
|
+
def self.execute(output,arguments=[])
|
6
|
+
|
7
|
+
Arver::Log.logger= IOLogger.new( output )
|
8
|
+
|
9
|
+
options = {
|
10
|
+
:user => '',
|
11
|
+
:config_dir => '',
|
12
|
+
:dry_run => false,
|
13
|
+
:test_mode => false,
|
14
|
+
:ask_password => false,
|
15
|
+
:force => false,
|
16
|
+
:violence => false,
|
17
|
+
:action => nil,
|
18
|
+
:argument => {},
|
19
|
+
}
|
20
|
+
|
21
|
+
parser = OptionParser.new do |opts|
|
22
|
+
opts.banner = <<-BANNER.gsub(/^ /,'')
|
23
|
+
arver.
|
24
|
+
|
25
|
+
Usage: #{File.basename($0)} [options] ACTION
|
26
|
+
|
27
|
+
Options:
|
28
|
+
BANNER
|
29
|
+
opts.on("-c", "--config-dir PATH", String,
|
30
|
+
"Path to config dir.",
|
31
|
+
"Default: .arver") { |arg| options[:config_dir] = arg }
|
32
|
+
opts.on("-u", "--user NAME", String,
|
33
|
+
"Username." ) { |arg| options[:user] = arg }
|
34
|
+
opts.on("-h", "--help",
|
35
|
+
"Show this help message.") { Arver::Log.write opts; return }
|
36
|
+
opts.on("--dry-run",
|
37
|
+
"Test your command.") { options[:dry_run] = true }
|
38
|
+
opts.on("--ask-password",
|
39
|
+
"Ask for Password when --add-user.") { options[:ask_password] = true }
|
40
|
+
opts.on("-t", "--trust-all",
|
41
|
+
"Use untrusted GPG Keys.") { options[:trust_all] = true }
|
42
|
+
opts.on("--force",
|
43
|
+
"Apply force (allow duplicate keys)") { options[:force] = true }
|
44
|
+
opts.on("--violence",
|
45
|
+
"Apply violence (allow destruction of disk)") { options[:violence] = true }
|
46
|
+
opts.on("-v",
|
47
|
+
"Verbose") { Arver::Log.level( Arver::LogLevels::Debug ) }
|
48
|
+
opts.on("--vv",
|
49
|
+
"Max Verbose") { Arver::Log.level( Arver::LogLevels::Trace ) }
|
50
|
+
opts.on( "-l", "--list-targets",
|
51
|
+
"List targets." ) { options[:action] = :list; }
|
52
|
+
opts.on( "-g", "--garbage-collect",
|
53
|
+
"Expunge old keys." ) { options[:action] = :gc; }
|
54
|
+
opts.on( "-k TARGET", "--keys TARGET", String,
|
55
|
+
"List local keys for this target.") { |arg| options[:argument][:target] = arg; options[:action] = :key_info; }
|
56
|
+
opts.on("--test-mode",
|
57
|
+
"Test mode (internal use)") { options[:test_mode] = true }
|
58
|
+
opts.separator "Targets:"
|
59
|
+
opts.on(
|
60
|
+
" Possible Paths are: 'Group', 'Host', 'Device', 'Host/Device',\n"+
|
61
|
+
" 'Group/Host/Device' or 'ALL'.\n"+
|
62
|
+
" Multiple Parameters can be given as comma separated list.\n"+
|
63
|
+
" Ambigues Target parameters will not be executed." )
|
64
|
+
opts.separator "Actions:"
|
65
|
+
opts.on_tail( "--create TARGET", String,
|
66
|
+
"Create new arver partition on Target." ) { |arg| options[:argument][:target] = arg; options[:action] = :create; }
|
67
|
+
opts.on_tail( "-o TARGET", "--open TARGET", String,
|
68
|
+
"Open target." ) { |arg| options[:argument][:target] = arg; options[:action] = :open; }
|
69
|
+
opts.on_tail( "-c TARGET", "--close TARGET", String,
|
70
|
+
"Close target." ) { |arg| options[:argument][:target] = arg; options[:action] = :close; }
|
71
|
+
opts.on_tail( "-a USER TARGET", "--add-user USER TARGET", String,
|
72
|
+
"Add a user to target.") { |user| options[:action] = :adduser; options[:argument][:user] = user; }
|
73
|
+
opts.on_tail( "-d USER TARGET", "--del-user USER TARGET", String,
|
74
|
+
"Remove a user from target.") { |user| options[:action] = :deluser; options[:argument][:user] = user; }
|
75
|
+
opts.on_tail( "-i TARGET", "--info TARGET", String,
|
76
|
+
"LUKS info about a target.") { |arg| options[:argument][:target] = arg; options[:action] = :info; }
|
77
|
+
|
78
|
+
begin
|
79
|
+
opts.parse!(arguments)
|
80
|
+
rescue
|
81
|
+
Arver::Log.write opts; return
|
82
|
+
end
|
83
|
+
|
84
|
+
if options[:action] == :deluser || options[:action] == :adduser
|
85
|
+
options[:argument][:target] = arguments.last
|
86
|
+
end
|
87
|
+
|
88
|
+
if options[:action].nil? ||
|
89
|
+
( options[:action] != :list && options[:action] != :gc && ! options[:argument][:target] ) ||
|
90
|
+
( ( options[:action] == :adduser || options[:action] == :deluser ) && ! options[:argument][:target] )
|
91
|
+
Arver::Log.write opts; return
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
unless( Arver::Bootstrap.run( options ) )
|
96
|
+
return
|
97
|
+
end
|
98
|
+
|
99
|
+
target_list = TargetList.get_list( options[:argument][:target] )
|
100
|
+
if target_list.empty? && ( options[:action] != :list && options[:action] != :gc )
|
101
|
+
Arver::Log.write( "No targets found" )
|
102
|
+
return false
|
103
|
+
end
|
104
|
+
|
105
|
+
run_action( options[:action], target_list, options[:argument][:user] )
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.run_action( action, target_list, target_user )
|
109
|
+
actions = {
|
110
|
+
:list => Arver::ListAction,
|
111
|
+
:gc => Arver::GCAction,
|
112
|
+
:create => Arver::CreateAction,
|
113
|
+
:open => Arver::OpenAction,
|
114
|
+
:close => Arver::CloseAction,
|
115
|
+
:adduser => Arver::AdduserAction,
|
116
|
+
:deluser => Arver::DeluserAction,
|
117
|
+
:info => Arver::InfoAction,
|
118
|
+
:key_info => Arver::KeyInfoAction,
|
119
|
+
}
|
120
|
+
|
121
|
+
action = (actions[ action ]).new( target_list )
|
122
|
+
|
123
|
+
return false unless( action.on_user( target_user ) )
|
124
|
+
|
125
|
+
catch ( :abort_action ) do
|
126
|
+
action.pre_action
|
127
|
+
action.run_on( Arver::Config.instance.tree )
|
128
|
+
action.post_action
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|