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,34 @@
1
+ module Arver
2
+
3
+ class IOLogger
4
+
5
+ include LogLevels
6
+
7
+ attr_accessor :level, :stream
8
+
9
+ def initialize( stream = $stdout, level = Info )
10
+ @level = level
11
+ @stream = stream
12
+ end
13
+
14
+ def trace( string )
15
+ write( string ) if level <= Trace
16
+ end
17
+ def debug( string )
18
+ write( string ) if level <= Debug
19
+ end
20
+ def info( string)
21
+ write( string ) if level <= Info
22
+ end
23
+ def warn( string )
24
+ write( string ) if level <= Warn
25
+ end
26
+ def error( string )
27
+ write( string ) if level <= Error
28
+ end
29
+ def write( string )
30
+ stream.write( string.to_s+"\n" )
31
+ stream.flush
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ module Arver
2
+ class KeyGenerator
3
+ def initialize
4
+ @keys = {}
5
+ end
6
+
7
+ def generate_key( user, partition )
8
+ key = ActiveSupport::SecureRandom.base64(192)
9
+ add( user, partition, key )
10
+ key
11
+ end
12
+
13
+ def add( user, partition, luks_key )
14
+ @keys[user] ||= {}
15
+ @keys[user][partition.path] = { :key => luks_key, :time => Time.now.to_f }
16
+ end
17
+
18
+ def remove_key( user, partition )
19
+ @keys[user].delete( partition.path )
20
+ end
21
+
22
+ def dump
23
+ @keys.each do | user, user_keys |
24
+ KeySaver.save( user, user_keys.to_yaml ) unless user_keys.empty?
25
+ end
26
+ @keys = {}
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module Arver
2
+ class KeyInfoAction < Action
3
+ def initialize(targets)
4
+ super(targets)
5
+ self.open_keystore
6
+ end
7
+ def pre_action
8
+ Arver::Log.write( "Listing keys: (+) available (-) not available: " )
9
+ end
10
+ def pre_host(h)
11
+ Arver::Log.write( " #{h.name}" )
12
+ end
13
+ def pre_partition(p)
14
+ if keystore.luks_key?(p)
15
+ line = " +"
16
+ else
17
+ line = " -"
18
+ end
19
+ versions = keystore.key_versions(p).collect do |v|
20
+ if v == 0
21
+ "0"
22
+ else
23
+ Date.strptime(v.to_s,'%s')
24
+ end
25
+ end
26
+ Arver::Log.write( "#{line} #{p.device_path} (#{versions.join(", ")})" )
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,128 @@
1
+ module Arver
2
+ class KeySaver
3
+
4
+ def self.save( user, key )
5
+ unless GPGKeyManager.check_key_of( user )
6
+ return
7
+ end
8
+ gpg_key = GPGKeyManager.key_of( user )
9
+ key = add_padding( key )
10
+ crypto = GPGME::Crypto.new :armor => true
11
+ begin
12
+ if( Arver::RuntimeConfig.instance.trust_all )
13
+ encrypted = crypto.encrypt( key, {:recipients => gpg_key.fingerprint, :always_trust => true})
14
+ else
15
+ encrypted = crypto.encrypt( key, {:recipients => gpg_key.fingerprint, :armor => true})
16
+ end
17
+ rescue GPGME::Error => gpgerr
18
+ Arver::Log.error( "GPGME Error #{gpgerr} Message: #{gpgerr.message}" )
19
+ return
20
+ end
21
+ key_encrypted = encrypted.read
22
+ unless( Arver::RuntimeConfig.instance.dry_run )
23
+ FileUtils.mkdir_p key_path(user) unless File.exists?( key_path(user) )
24
+ filename = key_path(user)+"/"+OpenSSL::Digest::SHA1.new(key_encrypted).to_s
25
+ File.open( filename, 'w' ) do |f|
26
+ f.write key_encrypted
27
+ end
28
+ end
29
+ filename
30
+ end
31
+
32
+ def self.key_path( user )
33
+ config_path+"/keys/"+user
34
+ end
35
+
36
+ def self.config_path
37
+ Arver::LocalConfig.instance.config_dir
38
+ end
39
+
40
+ def self.purge_keys( user )
41
+ FileUtils.rm_rf( key_path( user ) )
42
+ end
43
+
44
+ def self.num_of_key_files( user )
45
+ Dir.entries( key_path( user ) ).size - 2
46
+ end
47
+
48
+ def self.read( user )
49
+ GPGKeyManager.check_key_of( user )
50
+ return [] unless File.exists?( key_path( user ) )
51
+ decrypted = []
52
+ crypto = GPGME::Crypto.new
53
+ Dir.entries( key_path( user ) ).sort.each do | file |
54
+ unless( file == "." || file == ".." )
55
+ Arver::Log.trace( "Loading keyfile "+file )
56
+ key_encrypted = File.open( key_path( user )+"/"+file )
57
+ begin
58
+ decrypted_txt = crypto.decrypt( key_encrypted, { :passphrase_callback => method( :passfunc ) } )
59
+ rescue GPGME::Error => gpgerr
60
+ Arver::Log.error( "GPGME Error #{gpgerr} Message: #{gpgerr.message}" )
61
+ next
62
+ end
63
+ decrypted_key = substract_padding( decrypted_txt.read )
64
+ decrypted += [ decrypted_key ];
65
+ end
66
+ end
67
+ decrypted
68
+ end
69
+
70
+ def self.add_padding( key )
71
+ marker = "--"+ActiveSupport::SecureRandom.base64( 82 )
72
+ size = 450000
73
+ padding_size = size - key.size
74
+ if padding_size <= 0
75
+ padding_size = 0
76
+ Arver::Log.warn( "Warning: Your arver keys exceed the maximal padding size, therefore i can no longer disguise how many keys you possess.")
77
+ end
78
+ padding = ActiveSupport::SecureRandom.base64( padding_size )
79
+ marker +"\n"+ key + "\n" + marker + "\n" + padding
80
+ end
81
+
82
+ def self.substract_padding( key )
83
+ if( key.starts_with? '--- ' )
84
+ Arver::Log.warn( "Warning: you are using deprecated unpadded keyfiles. Please run garbage collect!" )
85
+ return key
86
+ end
87
+ marker = ""
88
+ striped_key = ""
89
+ key.each_line do |line|
90
+ if( marker.empty? )
91
+ marker = line
92
+ elsif( line == marker )
93
+ break
94
+ else
95
+ striped_key += line
96
+ end
97
+ end
98
+ striped_key.chomp
99
+ end
100
+ end
101
+ end
102
+
103
+
104
+ def passfunc(hook, uid_hint, passphrase_info, prev_was_bad, fd)
105
+ Arver::Log.write("Passphrase for #{uid_hint}: ")
106
+ begin
107
+ io = IO.for_fd(fd, 'w')
108
+ io.puts( ask("") { |q| q.echo = false } )
109
+ io.flush
110
+ ensure
111
+ (0 ... $_.length).each do |i| $_[i] = ?0 end if $_
112
+ end
113
+ Arver::Log.write("")
114
+ end
115
+
116
+ def testpassfunc(hook, uid_hint, passphrase_info, prev_was_bad, fd)
117
+ $stderr.write("Passphrase for #{uid_hint}: (test Mode) ")
118
+ $stderr.flush
119
+ begin
120
+ io = IO.for_fd(fd, 'w')
121
+ io.puts( "test" )
122
+ io.flush
123
+ ensure
124
+ (0 ... $_.length).each do |i| $_[i] = ?0 end if $_
125
+ end
126
+ $stderr.puts
127
+ end
128
+
@@ -0,0 +1,70 @@
1
+ module Arver
2
+ class Keystore
3
+
4
+ include Singleton
5
+
6
+ attr_accessor :username
7
+
8
+ def initialize
9
+ @keys = {}
10
+ @key_versions = {}
11
+ self.username= Arver::LocalConfig.instance.username
12
+ end
13
+
14
+ def load
15
+ flush_keys
16
+ KeySaver.read( self.username ).each do | loaded |
17
+ YAML.load( loaded ).each do | target, key |
18
+ load_luks_key(target,key)
19
+ end
20
+ end
21
+ end
22
+
23
+ def save
24
+ purge_keys
25
+ KeySaver.save(username, @keys.to_yaml)
26
+ end
27
+
28
+ def purge_keys
29
+ KeySaver.purge_keys( username )
30
+ end
31
+
32
+ def flush_keys
33
+ @keys = {}
34
+ end
35
+
36
+ def luks_key(partition)
37
+ @keys[partition.path][:key] unless ! @keys[partition.path]
38
+ end
39
+
40
+ def load_luks_key(partition, new_key)
41
+ if( new_key.kind_of? Hash )
42
+ if( ! @keys[partition] || @keys[partition][:time] <= new_key[:time] )
43
+ @keys[partition] = new_key
44
+ end
45
+ else
46
+ unless( @keys[partition] )
47
+ @keys[partition] = { :key => new_key, :time => 0.0 }
48
+ end
49
+ end
50
+ mark_key_version(partition,@keys[partition])
51
+ end
52
+
53
+ def mark_key_version(path,key)
54
+ @key_versions[path] ||= []
55
+ @key_versions[path] << key[:time]
56
+ end
57
+
58
+ def key_versions(partition)
59
+ @key_versions[partition.path] || []
60
+ end
61
+
62
+ def add_luks_key(partition, new_key)
63
+ @keys[partition.path] = { :key => new_key, :time => Time.new.to_f }
64
+ end
65
+
66
+ def luks_key?(partition)
67
+ ! @keys[partition.path].nil?
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,7 @@
1
+ module Arver
2
+ class ListAction < Action
3
+ def post_action
4
+ Arver::Log.write( Arver::Config.instance.tree.to_ascii )
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,41 @@
1
+ class Arver::LocalConfig
2
+ #this Config Object holds the local defaults for Arver. All options correspond to the ones set in .arver.local
3
+ include Singleton
4
+
5
+ def path
6
+ File.expand_path("~/.arver")
7
+ end
8
+
9
+ def config
10
+ @config ||= load_file( path )
11
+ end
12
+
13
+ def default
14
+ { 'config_dir' => "~/.arverdata", 'username' => "" }
15
+ end
16
+
17
+ def load_file(filename)
18
+ content = YAML.load(File.read(filename)) if File.exists?(filename)
19
+ self.default.merge(content||{})
20
+ end
21
+
22
+ def save
23
+ File.open( path, 'w' ) { |f| f.write(self.config.to_yaml) }
24
+ end
25
+
26
+ def username
27
+ self.config['username']
28
+ end
29
+
30
+ def username= username
31
+ self.config['username'] = username
32
+ end
33
+
34
+ def config_dir
35
+ File.expand_path(self.config['config_dir'])
36
+ end
37
+
38
+ def config_dir=(directory)
39
+ self.config['config_dir'] = directory
40
+ end
41
+ end
@@ -0,0 +1,39 @@
1
+ module Arver
2
+ class Log
3
+
4
+ include LogLevels
5
+
6
+ def self.logger()
7
+ @@logger ||= IOLogger.new
8
+ end
9
+ def self.logger=( logger )
10
+ @@logger = logger
11
+ end
12
+
13
+ def self.trace( string )
14
+ logger.trace( string )
15
+ end
16
+ def self.debug( string )
17
+ logger.debug( string )
18
+ end
19
+ def self.info( string )
20
+ logger.info( string )
21
+ end
22
+ def self.warn( string )
23
+ logger.warn( string )
24
+ end
25
+ def self.error( string )
26
+ logger.error( string )
27
+ end
28
+ def self.write( string )
29
+ logger.write( string )
30
+ end
31
+ def self.level( num )
32
+ logger.level=( num )
33
+ end
34
+
35
+
36
+ end
37
+ end
38
+
39
+
@@ -0,0 +1,9 @@
1
+ module Arver
2
+ module LogLevels
3
+ Trace = 1
4
+ Debug = 2
5
+ Info = 3
6
+ Warn = 4
7
+ Error = 5
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ module Arver
2
+ class LuksWrapper
3
+ def self.addKey( key_slot, partition )
4
+ Arver::SSHCommandWrapper.create( "cryptsetup", [ "--batch-mode", "--key-slot=#{key_slot}", "luksAddKey", partition.device_path ], partition.parent, true )
5
+ end
6
+ def self.close( partition )
7
+ Arver::SSHCommandWrapper.create( "cryptsetup", [ "luksClose", "#{partition.name}"], partition.parent, true )
8
+ end
9
+ def self.dump( partition )
10
+ Arver::SSHCommandWrapper.create( "cryptsetup", [ "luksDump", partition.device_path ], partition.parent, true )
11
+ end
12
+ def self.create( key_slot, partition )
13
+ Arver::SSHCommandWrapper.create( "cryptsetup", [ "--batch-mode", "--key-slot=#{key_slot}", "--cipher=aes-cbc-essiv:sha256", "--key-size=256", "luksFormat", partition.device_path ], partition.parent, true )
14
+ end
15
+ def self.killSlot( key_slot, partition )
16
+ Arver::SSHCommandWrapper.create( "cryptsetup", [ "--batch-mode", "luksKillSlot", partition.device_path, key_slot ], partition.parent, true )
17
+ end
18
+ def self.open( partition )
19
+ Arver::SSHCommandWrapper.create( "cryptsetup", [ "--batch-mode", "luksOpen", "-T 1", partition.device_path, partition.name ], partition.parent, true )
20
+ end
21
+ def self.open?( partition )
22
+ Arver::SSHCommandWrapper.create( "test", [ "-b", "/dev/mapper/#{partition.name}" ], partition.parent, true )
23
+ end
24
+ def self.was_wrong_key?( command_wrapper )
25
+ # before version 1.2 return value was 234
26
+ command_wrapper.return_value == 234 || command_wrapper.return_value == 2
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ module Arver
2
+ module NodeWithScriptHooks
3
+ attr_accessor :pre_open, :pre_close, :post_open, :post_close
4
+ def script_hooks_to_yaml
5
+ yaml = ""
6
+ yaml << "'pre_open': '#{pre_open}'\n" unless pre_open.nil?
7
+ yaml << "'pre_close': '#{pre_close}'\n" unless pre_close.nil?
8
+ yaml << "'post_open': '#{post_open}'\n" unless post_open.nil?
9
+ yaml << "'post_close': '#{post_close}'\n" unless post_close.nil?
10
+ yaml
11
+ end
12
+ def script_hooks_from_hash( hash )
13
+ hash.each do | name, data |
14
+ self.pre_open= data if name == "pre_open"
15
+ self.pre_close= data if name == "pre_close"
16
+ self.post_open= data if name == "post_open"
17
+ self.post_close= data if name == "post_close"
18
+ end
19
+ hash.delete("pre_open")
20
+ hash.delete("pre_close")
21
+ hash.delete("post_open")
22
+ hash.delete("post_close")
23
+ end
24
+ end
25
+ end