secretmgr 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,171 @@
1
+ require "pathname"
2
+ require "yaml"
3
+ require "optparse"
4
+
5
+ module Secretmgr
6
+ class Cli
7
+ EXIT_CODE_SUCCESS = 0
8
+ EXIT_CODE_FAILURE = 10
9
+
10
+ CLI_OPTION_SUCCESS = 100
11
+ CLI_OPTION_ERROR_NIL = 110
12
+ CLI_OPTION_ERROR_DOES_NOT_EXIST = 120
13
+ CLI_OPTION_ERROR_ZERO = 130
14
+
15
+ FILE_OPTION = 10
16
+ DIRECTORY_OPTION = 20
17
+
18
+ def initialize
19
+ # log_level = :debug
20
+ log_level = :info
21
+ Secretmgr.log_init(log_level)
22
+ # log_level = :info
23
+ Loggerxs.init("log_", "log.txt", ".", true, log_level)
24
+ @params = {}
25
+
26
+ @opt = OptionParser.new
27
+ @opt.banner = "usage: secretmgr2 [@option]"
28
+ @opt.on("-c", "--cmd command") { |v| @params[:cmd] = v }
29
+ @opt.on("-d", "--secret_dir dir") { |v| @params[:secret_dir] = v }
30
+ @opt.on("-s", "--global_setting_file file") { |v| @params[:global_setting_file] = v }
31
+ @opt.on("-u", "--public_keyfile file") { |v| @params[:public_keyfile] = v }
32
+ @opt.on("-r", "--private_keyfile file") { |v| @params[:private_keyfile] = v }
33
+ @opt.on("-k", "--secret_key_dir dir") { |v| @params[:secret_key_dir] = v }
34
+ @opt.on("-F", "--encrypted_setting_file file") { |v| @params[:encrypted_setting_file] = v }
35
+ @opt.on("-e", "--encrypted_secret_file file") { |v| @params[:encrypted_secret_file] = v }
36
+ @opt.on("-f", "--plain_setting_file file") { |v| @params[:plain_setting_file] = v }
37
+ @opt.on("-p", "--plain_secret_file file") { |v| @params[:plain_secret_file] = v }
38
+
39
+ @opt.on("-t", "--target word") { |v| @params[:target] = v }
40
+ @opt.on("-b", "--subtarget word") { |v| @params[:subtarget] = v }
41
+ end
42
+
43
+ def arg_parse(argv)
44
+ ret = true
45
+ @opt.parse!(argv)
46
+ Loggerxs.debug @params
47
+ @cmd = @params[:cmd]
48
+ @setting_file_pn = "#{Pathname.new(Dir.home)}.secretmgr.yml"
49
+
50
+ @secret_dir_pn = Pathname.new(@params[:secret_dir]) if @params[:secret_dir]
51
+ @global_setting_file_pn = Pathname.new(@params[:global_setting_file]) if @params[:global_setting_file]
52
+ @secret_key_dir_pn = Pathname.new(@params[:secret_key_dir]) if @params[:secret_key_dir]
53
+ @public_keyfile_pn = Pathname.new(@params[:public_keyfile]) if @params[:public_keyfile]
54
+ @private_keyfile_pn = Pathname.new(@params[:private_keyfile]) if @params[:private_keyfile]
55
+ @encrypted_setting_file_pn = Pathname.new(@params[:encrypted_setting_file]) if @params[:encrypted_setting_file]
56
+ @encrypted_secret_file_pn = Pathname.new(@params[:encrypted_secret_file]) if @params[:encrypted_secret_file]
57
+ @plain_setting_file_pn = Pathname.new(@params[:plain_setting_file]) if @params[:plain_setting_file]
58
+ @plain_secret_file_pn = Pathname.new(@params[:plain_secret_file]) if @params[:plain_secret_file]
59
+
60
+ @target = @params[:target]
61
+ @subtarget = @params[:subtarget]
62
+
63
+ # p "arg_parse @public_keyfile_pn=#{@public_keyfile_pn}"
64
+ # p "arg_parse @private_keyfile_pn=#{@private_keyfile_pn}"
65
+
66
+ fail_count = 0
67
+ fail_count += file_option_error?(@global_setting_file_pn, "-s")
68
+ fail_count += directory_option_error?(@secret_dir_pn, "-d")
69
+ fail_count += directory_option_error?(@secret_key_dir_pn, "-k")
70
+ # fail_count += file_option_error?(@public_keyfile_pn, "-u")
71
+ Loggerxs.debug "@private_keyfile_pn=#{@private_keyfile_pn}"
72
+ # fail_count += file_option_error?(@private_keyfile_pn, "-r")
73
+
74
+ if @cmd == "setup"
75
+ fail_count += file_option_error?(@plain_setting_file_pn, "-f")
76
+ fail_count += file_option_error?(@plain_secret_file_pn, "-p")
77
+ fail_count += file_specified_option_error?(@encrypted_setting_file_pn, "-F")
78
+ fail_count += file_specified_option_error?(@encrypted_secret_fifile_pn, "-R")
79
+ else
80
+ # debugger
81
+ @target = @params[:target]
82
+ @subtarget = @params[:subtarget]
83
+ fail_count += string_option_error?(@target, "-t")
84
+ fail_count += string_option_error?(@subtarget, "-b")
85
+ fail_count += file_option_error?(@encrypted_setting_file_pn, "-F")
86
+ fail_count += file_option_error?(@encrypted_secret_file_pn, "-e")
87
+ end
88
+
89
+ ret = false if fail_count.positive?
90
+ Loggerxs.debug "fail_count=#{fail_count}"
91
+ Loggerxs.debug "ret=#{ret}"
92
+ ret
93
+ end
94
+
95
+ def directory_option_error?(pathn, option_name)
96
+ path_option_error?(pathn, option_name, DIRECTORY_OPTION)
97
+ end
98
+
99
+ def file_option_error?(pathn, option_name)
100
+ path_option_error?(pathn, option_name, FILE_OPTION)
101
+ end
102
+
103
+ def file_specified_option_error?(pathn, option_name)
104
+ path_specified_option_error?(pathn, option_name, FILE_OPTION)
105
+ end
106
+
107
+ def path_specified_option_error?(pathn, _option_name, _kind = FILE_OPTION)
108
+ fail_count = 0
109
+ fail_count = 1 if pathn.nil?
110
+ fail_count
111
+ end
112
+
113
+ def path_option_error?(pathn, option_name, kind = FILE_OPTION)
114
+ fail_count = 0
115
+ if Util.nil_or_dontexist?(pathn)
116
+ if kind == FILE_OPTION
117
+ Loggerxs.error "Can't find file(#{pathn}) which specified by #{option_name}"
118
+ else
119
+ Loggerxs.error "Can't find directory(#{pathn}) which specified by #{option_name}"
120
+ end
121
+ fail_count = 1
122
+ elsif kind == FILE_OPTION
123
+ fail_count = 1 unless pathn.file?
124
+ elsif !pathn.directory?
125
+ fail_count = 1
126
+ end
127
+ fail_count
128
+ end
129
+
130
+ def string_option_error?(str, option_name)
131
+ fail_count = 0
132
+ if Util.nil_or_zero?(str)
133
+ Loggerxs.error "nil or size zero(#{str}) which specified by #{option_name}"
134
+ fail_count = 1
135
+ end
136
+ fail_count
137
+ end
138
+
139
+ def execute
140
+ ret = nil
141
+ @global_setting = Globalsetting.new(@global_setting_file_pn)
142
+ @global_setting.ensure
143
+ @global_setting.load
144
+
145
+ case @cmd
146
+ when "setup"
147
+ secretmgr = Secretmgr.new(@global_setting, @secret_dir_pn, @secret_key_dir_pn, "setup")
148
+ return EXIT_CODE_FAILURE unless secretmgr.valid?
149
+
150
+ Loggerxs.debug "setup 1"
151
+ # p "@plain_setting_file_pn=#{@plain_setting_file_pn}"
152
+ secretmgr.set_setting_for_plain(@plain_setting_file_pn, @plain_secret_file_pn)
153
+ Loggerxs.debug "setup 2"
154
+ ret = secretmgr.setup
155
+ Loggerxs.debug "setup ret=#{ret}"
156
+ else
157
+ # p "cli execute data @public_keyfile_pn=#{@public_keyfile_pn} @private_keyfile_pn=#{@private_keyfile_pn}"
158
+ secretmgr = Secretmgr.new(@global_setting, @secret_dir_pn, @secret_key_dir_pn, "data",
159
+ public_keyfile_pn: @public_keyfile_pn,
160
+ private_keyfile_pn: @private_keyfile_pn)
161
+ # secretmgr.set_setting_for_plain(@plain_setting_file_pn, @plain_secret_file_pn)
162
+ secretmgr.set_setting_for_encrypted(@encrypted_setting_file_pn, @encrypted_secret_file_pn)
163
+ secretmgr.set_setting_for_query(@target, @subtarget)
164
+ secretmgr.load
165
+ ret = secretmgr.make(@target, @subtarget)
166
+ # p ret
167
+ end
168
+ ret
169
+ end
170
+ end
171
+ end
@@ -1,41 +1,42 @@
1
- # frozen_string_literal: true
2
-
3
- require "pathname"
4
- require "yaml"
5
-
6
- module Secretmgr
7
- class Config
8
- def initialize(parent_pn, format_filename = "format.txt")
9
- format_pn = parent_pn + format_filename
10
- file_content = File.read(format_pn)
11
- @hash = YAML.safe_load(file_content)
12
- end
13
-
14
- def file_format(*keys)
15
- result = keys.flatten.each_with_object([@hash]) do |item, memo|
16
- hash = memo[0]
17
- memo[0] = if hash
18
- (hash.instance_of?(Hash) ? hash[item] : nil)
19
- end
20
- end
21
- result ? (result[0] || @hash["default"]) : @hash["default"]
22
- end
23
-
24
- def get_file_path(parent_dir_pn, *keys)
25
- flat_keys = keys.flatten
26
- valid_keys = flat_keys.reject(&:nil?)
27
- file_format = file_format(valid_keys)
28
- case file_format
29
- when "JSON_FILE"
30
- flat_keys.unshift("JSON_FILE")
31
- flat_keys.push("config.json")
32
- when "YAML"
33
- flat_keys = ["secret.yml"]
34
- end
35
- array = flat_keys.each_with_object([parent_dir_pn]) do |item, memo|
36
- memo[0] = memo[0] + item if item
37
- end
38
- array[0]
39
- end
40
- end
41
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "yaml"
5
+
6
+ module Secretmgr
7
+ class Config
8
+ def initialize(parent_pn, format_filename = "format.txt")
9
+ format_pn = Pathname.new(parent_pn) + format_filename
10
+ file_content = File.read(format_pn)
11
+ @hash = YAML.safe_load(file_content)
12
+ end
13
+
14
+ def file_format(*keys)
15
+ result = keys.flatten.each_with_object([@hash]) do |item, memo|
16
+ hash = memo[0]
17
+ memo[0] = if hash
18
+ (hash.instance_of?(Hash) ? hash[item] : nil)
19
+ end
20
+ end
21
+ Loggerxs.debug "hash=#{hash}"
22
+ result ? (result[0] || @hash["default"]) : @hash["default"]
23
+ end
24
+
25
+ def get_file_path(parent_dir_pn, *keys)
26
+ flat_keys = keys.flatten
27
+ valid_keys = flat_keys.compact
28
+ file_format = file_format(valid_keys)
29
+ case file_format
30
+ when "JSON_FILE"
31
+ flat_keys.unshift("JSON_FILE")
32
+ flat_keys.push("config.json")
33
+ when "YAML"
34
+ flat_keys = ["secret.yml"]
35
+ end
36
+ array = flat_keys.each_with_object([parent_dir_pn]) do |item, memo|
37
+ memo[0] = memo[0] + item if item
38
+ end
39
+ array[0]
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,49 @@
1
+ module Secretmgr
2
+ class Globalsetting
3
+ require "yaml"
4
+ require "pathname"
5
+
6
+ def initialize(file_pn)
7
+ @file_pn = file_pn
8
+ @obj = nil
9
+ end
10
+
11
+ def ensure
12
+ File.write(@file_pn, "") unless @file_pn.exist?
13
+ end
14
+
15
+ def load
16
+ # p "@file_pn=#{@file_pn}"
17
+ content = File.read(@file_pn)
18
+ @obj = YAML.safe_load(content)
19
+ @load ||= {}
20
+ # p "Globalsetting.load @obj=#{@obj}|"
21
+ end
22
+
23
+ def save
24
+ content = YAML.dump(@obj)
25
+ File.write(@file_pn, content)
26
+ p "Globalsetting.save @file_pn=#{@file_pn}|"
27
+ p "Globalsetting.save content=#{content}|"
28
+ end
29
+
30
+ def get(key)
31
+ case key
32
+ when "default_public_keyfile_pn", "default_private_keyfile_pn"
33
+ Pathname.new(@obj[key])
34
+ else
35
+ @obj[key]
36
+ end
37
+ end
38
+
39
+ def set(key, value)
40
+ @obj[key] = case key
41
+ when "default_public_keyfile_pn", "default_private_keyfile_pn"
42
+ value.to_s
43
+ # p "Globalsetting.set @obj=#{@obj}|"
44
+ else
45
+ value
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ module Secretmgr
2
+ class Loggerxs < Loggerx::Loggerxcm0
3
+ end
4
+ end
@@ -0,0 +1,228 @@
1
+ module Secretmgr
2
+ class Secret
3
+ require "rspec/expectations"
4
+
5
+ include RSpec::Matchers
6
+
7
+ RSA_KEY_SIZE = 2048
8
+
9
+ FORMAT_FILE = "format.txt".freeze
10
+ SSH_DIR = ".ssh".freeze
11
+ RSA_PRIVATE_FILE = "id_rsa_no".freeze
12
+ RSA_PUBLIC_PEM_FILE = "id_rsa_no.pub.pem".freeze
13
+ SETTING_FILE = "setting.yml".freeze
14
+ SECRET_FILE = "secret.yml".freeze
15
+ DEFAULT_PUBLIC_KEYFILE = ".ssh/id_rsa.pub".freeze
16
+ DEFAULT_PRIVATE_KEYFILE = ".ssh/id_rsa".freeze
17
+ attr_reader :public_key, :public_keyfile_pn, :private_key, :private_keyfile_pn, :valid
18
+
19
+ def initialize(setting, home_pn, secret_dir_pn, ope,
20
+ default_public_keyfile_pn,
21
+ default_private_keyfile_pn,
22
+ public_keyfile_pn: nil,
23
+ private_keyfile_pn: nil)
24
+ # p "Secret.initialize public_keyfile_pn=#{public_keyfile_pn}"
25
+ # p "Secret.initialize private_keyfile_pn~#{private_keyfile_pn}"
26
+ @mode = OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING
27
+ @setting = setting
28
+ # p "Secret.new secret_dir_pn=#{secret_dir_pn}"
29
+ @secret_dir_pn = secret_dir_pn
30
+ @secret_dir_pn = Pathname.new(@secret_dir_pn) unless @secret_dir_pn.instance_of?(Pathname)
31
+ # p "@Secret.new secret_dir_pn=#{@secret_dir_pn}"
32
+
33
+ @home_pn = home_pn
34
+ @format_config = Config.new(@secret_dir_pn, FORMAT_FILE)
35
+
36
+ @private_key = nil
37
+ @public_key = nil
38
+ @private_key = create_private_key(private_keyfile_pn) if private_keyfile_pn
39
+ @public_key = create_public_key(public_keyfile_pn) if public_keyfile_pn
40
+
41
+ @valid = false
42
+
43
+ if @private_key.nil? && @public_key.nil?
44
+ case ope
45
+ when "setup"
46
+ # @public_key, @private_key = create_keyfiles()
47
+ @rsa_key, @public_key, @public_key_str, @private_key, @private_key_str = create_keyfiles
48
+ default_public_keyfile_pn ||= @setting.get("default_public_keyfile_pn")
49
+ default_private_keyfile_pn ||= @setting.get("default_private_keyfile_pn")
50
+ output_public_key(default_public_keyfile_pn)
51
+ output_private_key(default_private_keyfile_pn)
52
+ @setting.set("default_public_keyfile_pn", default_public_keyfile_pn)
53
+ @setting.set("default_private_keyfile_pn", default_private_keyfile_pn)
54
+ @setting.save
55
+ else
56
+ default_public_keyfile_pn = @setting.get("default_public_keyfile_pn")
57
+ default_private_keyfile_pn = @setting.get("default_private_keyfile_pn")
58
+ @private_key = create_private_key(default_private_keyfile_pn)
59
+ @public_key = create_public_key(default_public_keyfile_pn)
60
+ end
61
+ end
62
+ @valid = true
63
+ end
64
+
65
+ def output_public_key(public_keyfile_pn)
66
+ File.write(public_keyfile_pn, @public_key_str)
67
+ Loggerxs.debug "0 public_keyfile_pn=#{public_keyfile_pn}"
68
+ end
69
+
70
+ def create_public_key(public_keyfile_pn)
71
+ key_obj = nil
72
+ pub_key = nil
73
+ pub_key = File.read(public_keyfile_pn) if public_keyfile_pn.exist?
74
+ Loggerxs.debug "0 public_keyfile_pn=#{public_keyfile_pn}"
75
+
76
+ unless pub_key.nil?
77
+ # 鍵をOpenSSLのオブジェクトにする
78
+ key_obj = OpenSSL::PKey::RSA.new(pub_key)
79
+ Loggerxs.debug "3 key_obj="
80
+ end
81
+ key_obj
82
+ end
83
+
84
+ def output_private_key(private_keyfile_pn)
85
+ File.write(private_keyfile_pn, @private_key_str)
86
+ Loggerxs.debug "0 private_keyfile_pn=#{private_keyfile_pn}"
87
+ end
88
+
89
+ def create_private_key(private_keyfile_pn)
90
+ key_obj = nil
91
+ private_key = nil
92
+ Loggerxs.debug "20 private_keyfile_pn=#{private_keyfile_pn}"
93
+ private_key = File.read(private_keyfile_pn) if private_keyfile_pn.exist?
94
+ unless private_key.nil?
95
+ # 鍵をOpenSSLのオブジェクトにする
96
+ key_obj = OpenSSL::PKey::RSA.new(private_key)
97
+ Loggerxs.debug "23 private_key="
98
+ end
99
+ key_obj
100
+ end
101
+
102
+ def file_format(target, sub_target)
103
+ @format_config.file_format(target, sub_target)
104
+ end
105
+
106
+ def get_file_path(dirs)
107
+ @format_config.get_file_path(@secret_dir_pn, dirs)
108
+ end
109
+
110
+ def make_pair_file_pn(file_pn, ext)
111
+ basename = file_pn.basename
112
+ extname = basename.extname
113
+ return nil if extname == ext
114
+
115
+ basename = file_pn.basename(".*")
116
+ @secret_dir_pn + %(#{basename}.#{ext})
117
+ end
118
+
119
+ def encrypted_setting_file_pn
120
+ @secret_dir_pn + SETTING_FILE
121
+ end
122
+
123
+ def encrypted_secret_file_pn
124
+ @secret_dir_pn + SECRET_FILE
125
+ end
126
+
127
+ def encrypt_with_public_key(data)
128
+ key = nil
129
+ if @public_key.nil?
130
+ return nil if @rsa_key.nil?
131
+
132
+ key = @rsa_key
133
+ else
134
+ key = @public_key
135
+ end
136
+ return unless key
137
+
138
+ # p "data.size=#{data.size}"
139
+ ecrypted_text = key.public_encrypt(
140
+ data,
141
+ @mode
142
+ )
143
+ Base64.encode64(ecrypted_text)
144
+ # # p "base64_test.size=#{base64_text.size}"
145
+ # #
146
+ # decrypted_text = @private_key_2.private_decrypt(
147
+ # ecrypted_text,
148
+ # @mode
149
+ # )
150
+ #
151
+ # # p "decrypted_text.size=#{decrypted_text.size}"
152
+ # # p "decrypted_text=#{decrypted_text}"
153
+ end
154
+
155
+ def encrypt_and_copy(src_pn, relative_path, key, ivx)
156
+ dest_pn = @secret_dir_pn + relative_path
157
+ return unless src_pn.exist? && src_pn.file?
158
+
159
+ dest_parent_pn = dest_pn.parent
160
+ dest_parent_pn.mkpath
161
+
162
+ plaintext = File.read(src_pn)
163
+ encrypted_text = encrypt_with_common_key(plaintext, key, ivx)
164
+ File.write(dest_pn, encrypted_text)
165
+ end
166
+
167
+ def decrypt_with_private_key(base64_text)
168
+ key = nil
169
+ if @private_key.nil?
170
+ return nil if @rsa_key.nil?
171
+
172
+ key = @rsa_key
173
+ else
174
+ key = @private_key
175
+ end
176
+ return unless key
177
+
178
+ plain_text = Base64.decode64(base64_text)
179
+ # p "decrypt_with_private_key base64_text.size=#{base64_text.size}"
180
+ # p "decrypt_with_private_key base64_text=#{base64_text}"
181
+ # p "decrypt_with_private_key @private_key=#{@private_key}"
182
+ # p "decrypt_with_private_key @rsa_key=#{@rsa_key}"
183
+ # p "decrypt_with_private_key key=#{key}"
184
+ # p "decrypt_with_private_key plain_text.size=#{plain_text.size}"
185
+ key.private_decrypt(
186
+ plain_text,
187
+ @mode
188
+ )
189
+ end
190
+
191
+ # 引数 plaintext を暗号化した結果を返す
192
+ def encrypt_with_common_key(plaintext, key, ivalue)
193
+ encx = OpenSSL::Cipher.new(CIPHER_NAME)
194
+ encx.encrypt
195
+ encx.key = key
196
+ encx.iv = ivalue
197
+ # str に与えた文字列を暗号化します。
198
+ encrypted_text = encx.update(plaintext) + encx.final
199
+
200
+ Base64.encode64(encrypted_text)
201
+ end
202
+
203
+ def decrypt_with_common_key(encrypted_data, key, ivalue)
204
+ decx = OpenSSL::Cipher.new(CIPHER_NAME)
205
+ decx.decrypt
206
+ decx.key = key
207
+ decx.iv = ivalue
208
+ data = decx.update(encrypted_data)
209
+ final_data = decx.final
210
+ decrypted_data = data + final_data
211
+ decrypted_data.force_encoding("UTF-8")
212
+ end
213
+
214
+ def create_keyfiles
215
+ rsa_key = OpenSSL::PKey::RSA.new(RSA_KEY_SIZE)
216
+ # 秘密鍵を生成
217
+ private_key = rsa_key
218
+ private_key_str = rsa_key.to_pem
219
+
220
+ # 公開鍵を生成
221
+ public_key = rsa_key.public_key
222
+ public_key_str = public_key.to_pem
223
+
224
+ Loggerxs.debug "############## create_keyfiles public_key=#{public_key}"
225
+ [rsa_key, public_key, public_key_str, private_key, private_key_str]
226
+ end
227
+ end
228
+ end