carioca 2.1.4 → 2.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -4
- data/Gemfile +0 -1
- data/README.md +53 -0
- data/Rakefile +4 -4
- data/VERSION +1 -1
- data/carioca.gemspec +4 -4
- data/lib/carioca/configuration.rb +1 -1
- data/lib/carioca/constants.rb +9 -12
- data/lib/carioca/dependencies.rb +0 -4
- data/lib/carioca/mixin.rb +4 -4
- data/lib/carioca/registry.rb +8 -8
- data/lib/carioca/registry_file.rb +2 -2
- data/lib/carioca/services/debug.rb +3 -3
- data/lib/carioca/services/finisher.rb +108 -115
- data/lib/carioca/services/init.rb +1 -1
- data/lib/carioca/services/output.rb +6 -6
- data/lib/carioca/services/sanitycheck.rb +90 -106
- data/lib/carioca/services/securestore.rb +74 -75
- data/lib/carioca/services/setup.rb +55 -60
- data/lib/carioca/services/toolbox.rb +93 -94
- data/samples/Gemfile +4 -2
- data/samples/test.rb +38 -45
- metadata +17 -17
@@ -1,143 +1,127 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# base Carioca namespace
|
4
4
|
module Carioca
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
module Services
|
6
|
+
# Exiter namespace
|
7
|
+
class Sanitycheck
|
8
|
+
def initialize
|
9
|
+
registry = Carioca::Registry.get
|
10
|
+
@output = registry.get_service name: :output
|
11
|
+
@i18n = registry.get_service name: :i18n
|
12
|
+
@toolbox = registry.get_service name: :toolbox
|
13
|
+
@configuration = registry.get_service name: :configuration
|
14
|
+
@finisher = registry.get_service name: :finisher
|
15
|
+
@schema = {}
|
16
|
+
if @configuration.settings.include? :sanitycheck
|
17
|
+
@schema = @configuration.settings.sanitycheck.include?(:rules) ? @configuration.settings.sanitycheck.rules : {}
|
18
|
+
end
|
19
|
+
end
|
10
20
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
def run
|
22
|
+
unless @schema.empty?
|
23
|
+
begin
|
24
|
+
@output.info @i18n.t('sanitycheck.run.start')
|
25
|
+
error_number = 0
|
26
|
+
@schema.each do |item|
|
27
|
+
testcase = item[:test]
|
28
|
+
item.delete(:test)
|
29
|
+
res = send(testcase, **item)
|
30
|
+
if res.empty?
|
31
|
+
@output.ok @i18n.t('sanitycheck.run.ok', testcase:, name: item[:name].to_s)
|
32
|
+
else
|
33
|
+
pbm = res.map(&:to_s).join(',')
|
34
|
+
@output.ko @i18n.t('sanitycheck.run.ko', testcase:, name: item[:name].to_s, pbm:)
|
35
|
+
error_number = + 1
|
36
|
+
end
|
22
37
|
end
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
testcase = item[:test] ; item.delete(:test)
|
31
|
-
res = self.send(testcase, **item)
|
32
|
-
if res.empty? then
|
33
|
-
@output.ok @i18n.t('sanitycheck.run.ok', testcase: testcase, name: item[:name].to_s)
|
34
|
-
else
|
35
|
-
pbm = res.map {|p| p.to_s}.join(',')
|
36
|
-
@output.ko @i18n.t('sanitycheck.run.ko', testcase: testcase, name: item[:name].to_s, pbm: pbm)
|
37
|
-
error_number =+ 1
|
38
|
-
end
|
39
|
-
end
|
40
|
-
if error_number>0 then
|
41
|
-
@output.error @i18n.t('sanitycheck.failure')
|
42
|
-
else
|
43
|
-
@output.success @i18n.t('sanitycheck.success')
|
44
|
-
end
|
45
|
-
rescue Exception
|
46
|
-
@finisher.secure_raise message: @i18n.t('sanitychek.error'), error_case: :status_ko
|
47
|
-
end unless @schema.empty?
|
48
|
-
|
38
|
+
if error_number.positive?
|
39
|
+
@output.error @i18n.t('sanitycheck.failure')
|
40
|
+
else
|
41
|
+
@output.success @i18n.t('sanitycheck.success')
|
42
|
+
end
|
43
|
+
rescue StandardError
|
44
|
+
@finisher.secure_raise message: @i18n.t('sanitychek.error'), error_case: :status_ko
|
49
45
|
end
|
46
|
+
end
|
47
|
+
end
|
50
48
|
|
49
|
+
# @!group Verifiers for application : FS and TCP/IP services
|
51
50
|
|
51
|
+
# check folder
|
52
|
+
# @return [Array] of Symbol with error type : [:inexistant,:mode,:owner,:group]
|
53
|
+
# @option [String] :name folder path (relative or absolute)
|
54
|
+
# @option [String] :mode String for OCTAL rights like "644", default 755
|
55
|
+
# @option [String] :owner file owner for folder, optionnal
|
56
|
+
# @option [String] :group file group for folder, optionnal
|
57
|
+
def verify_folder(name:, mode: '755', owner: nil, group: nil)
|
58
|
+
full_name = File.expand_path(name)
|
59
|
+
res = []
|
60
|
+
return [:inexistant] unless File.directory?(full_name)
|
52
61
|
|
53
|
-
#@!group Verifiers for application : FS and TCP/IP services
|
54
|
-
|
55
|
-
# check folder
|
56
|
-
# @return [Array] of Symbol with error type : [:inexistant,:mode,:owner,:group]
|
57
|
-
# @option [String] :name folder path (relative or absolute)
|
58
|
-
# @option [String] :mode String for OCTAL rights like "644", default 755
|
59
|
-
# @option [String] :owner file owner for folder, optionnal
|
60
|
-
# @option [String] :group file group for folder, optionnal
|
61
|
-
def verify_folder(name:, mode: "755", owner: nil, group: nil)
|
62
|
-
full_name = File.expand_path(name)
|
63
|
-
res = Array::new
|
64
|
-
return [:inexistant] unless File.directory?(full_name)
|
65
62
|
stat = File.stat(full_name)
|
66
|
-
if mode
|
67
|
-
tested_mode =
|
68
|
-
res << :mode if tested_mode[-3
|
63
|
+
if mode
|
64
|
+
tested_mode = format('%o', stat.mode)
|
65
|
+
res << :mode if tested_mode[-3..] != mode
|
69
66
|
end
|
70
|
-
if owner
|
71
|
-
|
72
|
-
|
73
|
-
if group then
|
74
|
-
res << :group if Etc.getgrgid(stat.gid).name != group
|
75
|
-
end
|
76
|
-
return res
|
67
|
+
res << :owner if owner && (Etc.getpwuid(stat.uid).name != owner)
|
68
|
+
res << :group if group && (Etc.getgrgid(stat.gid).name != group)
|
69
|
+
res
|
77
70
|
end
|
78
|
-
|
71
|
+
|
79
72
|
# check symlink
|
80
73
|
# @return [Boolean]
|
81
74
|
# @option [String] :name path of the link
|
82
|
-
def verify_link(name:
|
83
|
-
full_name = File.expand_path(name)
|
84
|
-
res =
|
75
|
+
def verify_link(name:)
|
76
|
+
full_name = File.expand_path(name)
|
77
|
+
res = []
|
85
78
|
res.push :inexistant unless File.file?(full_name)
|
86
|
-
|
79
|
+
res
|
87
80
|
end
|
88
|
-
|
81
|
+
|
89
82
|
# check file
|
90
83
|
# @return [Array] of Symbol with error type : [:inexistant,:mode,:owner,:group]
|
91
84
|
# @option [String] :name path of file
|
92
85
|
# @option [String] :mode String for OCTAL rights like "644", optionnal
|
93
86
|
# @option [String] :owner file owner for file, optionnal
|
94
87
|
# @option [String] :group file group for file, optionnal
|
95
|
-
def verify_file(name
|
88
|
+
def verify_file(name:, mode: '644', owner: nil, group: nil)
|
96
89
|
full_name = File.expand_path(name)
|
97
|
-
res =
|
98
|
-
return
|
90
|
+
res = []
|
91
|
+
return [:inexistant] unless File.file?(full_name)
|
92
|
+
|
99
93
|
stat = File.stat(full_name)
|
100
|
-
if mode
|
101
|
-
tested_mode =
|
102
|
-
res << :mode if tested_mode[-3
|
94
|
+
if mode
|
95
|
+
tested_mode = format('%o', stat.mode)
|
96
|
+
res << :mode if tested_mode[-3..] != mode
|
103
97
|
end
|
104
|
-
if owner
|
105
|
-
|
106
|
-
|
107
|
-
if group then
|
108
|
-
res << :group if Etc.getgrgid(stat.gid).name != group
|
109
|
-
end
|
110
|
-
return res
|
98
|
+
res << :owner if owner && (Etc.getpwuid(stat.uid).name != owner)
|
99
|
+
res << :group if group && (Etc.getgrgid(stat.gid).name != group)
|
100
|
+
res
|
111
101
|
end
|
112
|
-
|
102
|
+
|
113
103
|
# TCP/IP service checker
|
114
104
|
# @return [Bool] status
|
115
|
-
# @option [String] :name display name
|
116
105
|
# @option [String] :host hostname
|
117
106
|
# @option [String] :port TCP port
|
118
107
|
# @option [String] :url full URL, priority on :host and :port
|
119
|
-
def verify_service(
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
return true
|
131
|
-
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
132
|
-
return false
|
133
|
-
end
|
134
|
-
end
|
135
|
-
rescue Timeout::Error
|
108
|
+
def verify_service(url: nil, host: nil, port: nil)
|
109
|
+
if url
|
110
|
+
uri = URI.parse(url)
|
111
|
+
host = uri.host
|
112
|
+
port = uri.port
|
113
|
+
end
|
114
|
+
Timeout.timeout(1) do
|
115
|
+
s = TCPSocket.new(host, port)
|
116
|
+
s.close
|
117
|
+
return true
|
118
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
136
119
|
return false
|
137
120
|
end
|
121
|
+
rescue Timeout::Error
|
122
|
+
false
|
138
123
|
end
|
139
|
-
|
140
|
-
|
141
|
-
end
|
124
|
+
# !@endgroup
|
142
125
|
end
|
126
|
+
end
|
143
127
|
end
|
@@ -1,81 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Carioca
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
attr_accessor :data
|
7
|
-
|
8
|
-
extend Carioca::Injector
|
9
|
-
inject service: :logger
|
10
|
-
inject service: :setup
|
11
|
-
inject service: :sanitycheck
|
4
|
+
module Services
|
5
|
+
class SecureStore
|
6
|
+
attr_accessor :data
|
12
7
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
decipher.decrypt
|
57
|
-
encoded_key = File.read(@keyfile)
|
58
|
-
key, iv = Base64.decode64(encoded_key).split('|')
|
59
|
-
decipher.key = key
|
60
|
-
decipher.iv = iv
|
61
|
-
encoded = File.read(@storefile)
|
62
|
-
encrypted = Base64.decode64(encoded)
|
63
|
-
plain = decipher.update(encrypted) + decipher.final
|
64
|
-
YAML.load(plain)
|
65
|
-
end
|
66
|
-
|
67
|
-
def encrypt(data)
|
68
|
-
encoded_key = File.read(@keyfile)
|
69
|
-
key, iv = Base64.decode64(encoded_key).split('|')
|
70
|
-
cipher = OpenSSL::Cipher.new('aes-256-cbc')
|
71
|
-
cipher.encrypt
|
72
|
-
cipher.key = key
|
73
|
-
cipher.iv = iv
|
74
|
-
encrypted = cipher.update(data.to_yaml) + cipher.final
|
75
|
-
encoded = Base64.encode64(encrypted)
|
76
|
-
File.write(@storefile, encoded)
|
77
|
-
end
|
8
|
+
extend Carioca::Injector
|
9
|
+
inject service: :logger
|
10
|
+
inject service: :setup
|
11
|
+
inject service: :sanitycheck
|
12
|
+
|
13
|
+
def initialize(storefile: Carioca::Registry.config.secure_store_file, keyfile: Carioca::Registry.config.master_key_file)
|
14
|
+
[storefile, keyfile].map { |file| File.dirname(file) }.each do |path|
|
15
|
+
setup.make_folder path: File.expand_path(path), mode: '400' unless sanitycheck.verify_folder name: path
|
16
|
+
end
|
17
|
+
@storefile = File.expand_path(storefile)
|
18
|
+
@keyfile = File.expand_path(keyfile)
|
19
|
+
init! unless initialized?
|
20
|
+
@data = decrypt
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialized?
|
24
|
+
File.exist?(@storefile) && File.exist?(@keyfile)
|
25
|
+
end
|
26
|
+
|
27
|
+
def save!
|
28
|
+
encrypt(@data)
|
29
|
+
end
|
30
|
+
|
31
|
+
def init!
|
32
|
+
path = File.dirname(@storefile)
|
33
|
+
FileUtils.mkdir_p path
|
34
|
+
generate_key
|
35
|
+
init_data = {}
|
36
|
+
encrypt(init_data)
|
37
|
+
logger.warn(to_s) { 'Secure Store initialized' }
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def generate_key
|
43
|
+
cipher = OpenSSL::Cipher.new('aes-256-cbc')
|
44
|
+
cipher.encrypt
|
45
|
+
key = cipher.random_key
|
46
|
+
iv = cipher.random_iv
|
47
|
+
encoded_key = Base64.encode64("#{key}|#{iv}")
|
48
|
+
unless File.exist? @keyfile
|
49
|
+
File.write(@keyfile, encoded_key)
|
50
|
+
FileUtils.chmod 0o400, @keyfile
|
78
51
|
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def decrypt
|
55
|
+
decipher = OpenSSL::Cipher.new('aes-256-cbc')
|
56
|
+
decipher.decrypt
|
57
|
+
encoded_key = File.read(@keyfile)
|
58
|
+
key, iv = Base64.decode64(encoded_key).split('|')
|
59
|
+
decipher.key = key
|
60
|
+
decipher.iv = iv
|
61
|
+
encoded = File.read(@storefile)
|
62
|
+
encrypted = Base64.decode64(encoded)
|
63
|
+
plain = decipher.update(encrypted) + decipher.final
|
64
|
+
YAML.safe_load(plain)
|
65
|
+
end
|
66
|
+
|
67
|
+
def encrypt(data)
|
68
|
+
encoded_key = File.read(@keyfile)
|
69
|
+
key, iv = Base64.decode64(encoded_key).split('|')
|
70
|
+
cipher = OpenSSL::Cipher.new('aes-256-cbc')
|
71
|
+
cipher.encrypt
|
72
|
+
cipher.key = key
|
73
|
+
cipher.iv = iv
|
74
|
+
encrypted = cipher.update(data.to_yaml) + cipher.final
|
75
|
+
encoded = Base64.encode64(encrypted)
|
76
|
+
File.write(@storefile, encoded)
|
77
|
+
end
|
79
78
|
end
|
79
|
+
end
|
80
80
|
end
|
81
|
-
|
@@ -1,87 +1,82 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# base Carioca namespace
|
4
4
|
module Carioca
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@schema = (@configuration.settings.setup.include? :rules)? @configuration.settings.setup.rules : {}
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
5
|
+
module Services
|
6
|
+
# Exiter namespace
|
7
|
+
class Setup
|
8
|
+
def initialize
|
9
|
+
registry = Carioca::Registry.get
|
10
|
+
@output = registry.get_service name: :output
|
11
|
+
@i18n = registry.get_service name: :i18n
|
12
|
+
@toolbox = registry.get_service name: :toolbox
|
13
|
+
@configuration = registry.get_service name: :configuration
|
14
|
+
@finisher = registry.get_service name: :finisher
|
15
|
+
@schema = {}
|
16
|
+
if @configuration.settings.include? :setup
|
17
|
+
@schema = @configuration.settings.setup.include?(:rules) ? @configuration.settings.setup.rules : {}
|
18
|
+
end
|
19
|
+
end
|
24
20
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@finisher.secure_raise message: @i18n.t('setup.error'), error_case: :status_ko
|
34
|
-
end unless @schema.empty?
|
21
|
+
def execute!
|
22
|
+
unless @schema.empty?
|
23
|
+
begin
|
24
|
+
@output.info @i18n.t('setup.execute.start')
|
25
|
+
@schema.each do |item|
|
26
|
+
action = item[:action]
|
27
|
+
item.delete(:action)
|
28
|
+
send action, **item
|
35
29
|
end
|
30
|
+
rescue StandardError
|
31
|
+
@finisher.secure_raise message: @i18n.t('setup.error'), error_case: :status_ko
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
36
35
|
|
36
|
+
# @!group facilities for file system commands
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
# @option [Bool] :gem resolve file in gem root (default true)
|
50
|
-
# @option [String] :gem_name name of the gem where to search (default "carioca")
|
51
|
-
def install_file(source:, target:, mode: "644", owner: nil, group: nil, force: true, gem: true, gem_name: "carioca" )
|
52
|
-
@output.item @i18n.t('setup.install', file: target)
|
38
|
+
# facility for file installation
|
39
|
+
# @option [String] :source file source path
|
40
|
+
# @option [String] :target file target path
|
41
|
+
# @option [String] :mode String for OCTAL rights (default "644")
|
42
|
+
# @option [String] :owner file owner for target (optional)
|
43
|
+
# @option [String] :group file group for target (optional)
|
44
|
+
# @option [Bool] :force to copy file in force (default true)
|
45
|
+
# @option [Bool] :gem resolve file in gem root (default true)
|
46
|
+
# @option [String] :gem_name name of the gem where to search (default "carioca")
|
47
|
+
def install_file(source:, target:, mode: '644', owner: nil, group: nil, force: true, gem: true, gem_name: 'carioca')
|
48
|
+
@output.item @i18n.t('setup.install', file: target)
|
53
49
|
full_target = File.expand_path(target)
|
54
|
-
source =
|
55
|
-
FileUtils
|
50
|
+
source = @toolbox.search_file_in_gem(gem: gem_name, file: source) if gem
|
51
|
+
FileUtils.copy source, full_target if force
|
56
52
|
FileUtils.chmod mode.to_i(8), full_target
|
57
|
-
FileUtils.chown owner, group, full_target if owner
|
53
|
+
FileUtils.chown owner, group, full_target if owner && group
|
58
54
|
end
|
59
|
-
|
55
|
+
|
60
56
|
# facility for folder creation
|
61
57
|
# @option [String] :path folder path (relative or absolute)
|
62
58
|
# @option [String] :mode String for OCTAL rights like "644"
|
63
59
|
# @option [String] :owner file owner for folder
|
64
60
|
# @option [String] :group file group for folder
|
65
|
-
|
61
|
+
def make_folder(path:, mode: '644', owner: nil, group: nil)
|
66
62
|
full_path = File.expand_path(path)
|
67
63
|
@output.item @i18n.t('setup.mkdir', path: full_path)
|
68
|
-
FileUtils
|
64
|
+
FileUtils.mkdir_p path unless File.exist? full_path
|
69
65
|
FileUtils.chmod mode.to_i(8), full_path
|
70
|
-
FileUtils.chown owner, group, full_path if owner
|
66
|
+
FileUtils.chown owner, group, full_path if owner && group
|
71
67
|
end
|
72
|
-
|
68
|
+
|
73
69
|
# facility for Symbolic link
|
74
70
|
# @option [String] :source path of the file
|
75
71
|
# @option [String] :link path of the symlink
|
76
72
|
def make_link(source:, link:)
|
77
|
-
|
78
|
-
|
79
|
-
@output.item @i18n.t('setup.ln', target: link, source:
|
80
|
-
FileUtils
|
81
|
-
FileUtils
|
73
|
+
File.expand_path(source)
|
74
|
+
File.expand_path(link)
|
75
|
+
@output.item @i18n.t('setup.ln', target: link, source:)
|
76
|
+
FileUtils.rm link if File.symlink?(link) && !File.exist?(link)
|
77
|
+
FileUtils.ln_s source, link unless File.exist? link
|
82
78
|
end
|
83
79
|
# @!endgroup
|
84
|
-
|
85
|
-
end
|
86
80
|
end
|
81
|
+
end
|
87
82
|
end
|