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.
Files changed (42) hide show
  1. data/CHANGELOG.textile +9 -0
  2. data/README.textile +120 -0
  3. data/bin/arver +10 -0
  4. data/lib/arver.rb +6 -0
  5. data/lib/arver/action.rb +78 -0
  6. data/lib/arver/adduser_action.rb +54 -0
  7. data/lib/arver/bootstrap.rb +35 -0
  8. data/lib/arver/cli.rb +133 -0
  9. data/lib/arver/close_action.rb +61 -0
  10. data/lib/arver/command_wrapper.rb +42 -0
  11. data/lib/arver/config.rb +54 -0
  12. data/lib/arver/create_action.rb +58 -0
  13. data/lib/arver/deluser_action.rb +34 -0
  14. data/lib/arver/gc_action.rb +12 -0
  15. data/lib/arver/gpg_key_manager.rb +80 -0
  16. data/lib/arver/host.rb +74 -0
  17. data/lib/arver/hostgroup.rb +17 -0
  18. data/lib/arver/info_action.rb +22 -0
  19. data/lib/arver/io_logger.rb +34 -0
  20. data/lib/arver/key_generator.rb +29 -0
  21. data/lib/arver/key_info_action.rb +29 -0
  22. data/lib/arver/key_saver.rb +128 -0
  23. data/lib/arver/keystore.rb +70 -0
  24. data/lib/arver/list_action.rb +7 -0
  25. data/lib/arver/local_config.rb +41 -0
  26. data/lib/arver/log.rb +39 -0
  27. data/lib/arver/log_levels.rb +9 -0
  28. data/lib/arver/luks_wrapper.rb +29 -0
  29. data/lib/arver/node_with_script_hooks.rb +25 -0
  30. data/lib/arver/open_action.rb +70 -0
  31. data/lib/arver/partition.rb +53 -0
  32. data/lib/arver/partition_hierarchy_node.rb +112 -0
  33. data/lib/arver/runtime_config.rb +22 -0
  34. data/lib/arver/ssh_command_wrapper.rb +21 -0
  35. data/lib/arver/string.rb +8 -0
  36. data/lib/arver/target_list.rb +33 -0
  37. data/lib/arver/test_config_loader.rb +21 -0
  38. data/lib/arver/test_partition.rb +9 -0
  39. data/lib/arver/tree.rb +32 -0
  40. data/lib/arver/version.rb +3 -0
  41. data/man/arver.5 +429 -0
  42. metadata +155 -0
@@ -0,0 +1,9 @@
1
+ === 0.0.4 2012-04-30
2
+
3
+ * Updated to run with ruby 1.9
4
+ * Validate gpg fingerprints
5
+
6
+ === 0.0.1 2010-07-11
7
+
8
+ * 1 major enhancement:
9
+ * Initial release
@@ -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.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created by arver on 2010-7-11.
4
+ # Copyright (c) 2010. All rights reserved.
5
+
6
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
7
+ require 'arver'
8
+ require "arver/cli"
9
+
10
+ Arver::CLI.execute($stdout,ARGV)
@@ -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
+
@@ -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
@@ -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