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,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