secretmgr 0.1.0 → 0.2.0

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.
@@ -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