arver 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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