opensecret 0.0.988 → 0.0.9925
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.
- checksums.yaml +5 -5
- data/README.md +56 -159
- data/bin/opensecret +2 -2
- data/bin/ops +17 -2
- data/lib/extension/string.rb +14 -16
- data/lib/{interpreter.rb → interprete.rb} +53 -29
- data/lib/keytools/binary.map.rb +49 -0
- data/lib/keytools/kdf.api.rb +249 -0
- data/lib/keytools/kdf.bcrypt.rb +64 -29
- data/lib/keytools/kdf.pbkdf2.rb +92 -83
- data/lib/keytools/kdf.scrypt.rb +190 -0
- data/lib/keytools/key.64.rb +326 -0
- data/lib/keytools/key.algo.rb +109 -0
- data/lib/keytools/key.api.rb +1281 -0
- data/lib/keytools/key.db.rb +265 -0
- data/lib/keytools/{key.module.rb → key.docs.rb} +55 -0
- data/lib/keytools/key.error.rb +110 -0
- data/lib/keytools/key.id.rb +271 -0
- data/lib/keytools/key.iv.rb +107 -0
- data/lib/keytools/key.local.rb +265 -0
- data/lib/keytools/key.mach.rb +248 -0
- data/lib/keytools/key.now.rb +402 -0
- data/lib/keytools/key.pair.rb +259 -0
- data/lib/keytools/key.pass.rb +120 -0
- data/lib/keytools/key.rb +428 -298
- data/lib/keytools/keydebug.txt +295 -0
- data/lib/logging/gem.logging.rb +3 -3
- data/lib/modules/cryptology/collect.rb +20 -0
- data/lib/session/require.gem.rb +1 -1
- data/lib/usecase/cmd.rb +417 -0
- data/lib/usecase/id.rb +36 -0
- data/lib/usecase/import.rb +174 -0
- data/lib/usecase/init.rb +78 -0
- data/lib/usecase/login.rb +70 -0
- data/lib/usecase/logout.rb +30 -0
- data/lib/usecase/open.rb +126 -0
- data/lib/{interprete → usecase}/put.rb +100 -47
- data/lib/usecase/read.rb +89 -0
- data/lib/{interprete → usecase}/safe.rb +0 -0
- data/lib/{interprete → usecase}/set.rb +0 -0
- data/lib/usecase/token.rb +111 -0
- data/lib/{interprete → usecase}/use.rb +0 -0
- data/lib/version.rb +1 -1
- data/opensecret.gemspec +4 -3
- metadata +39 -33
- data/lib/exception/cli.error.rb +0 -53
- data/lib/exception/errors/cli.errors.rb +0 -31
- data/lib/interprete/begin.rb +0 -232
- data/lib/interprete/cmd.rb +0 -621
- data/lib/interprete/export.rb +0 -163
- data/lib/interprete/init.rb +0 -205
- data/lib/interprete/key.rb +0 -119
- data/lib/interprete/open.rb +0 -148
- data/lib/interprete/seal.rb +0 -129
- data/lib/keytools/digester.rb +0 -245
- data/lib/keytools/key.data.rb +0 -227
- data/lib/keytools/key.derivation.rb +0 -341
- data/lib/modules/mappers/collateral.rb +0 -282
- data/lib/modules/mappers/envelope.rb +0 -127
- data/lib/modules/mappers/settings.rb +0 -170
- data/lib/notepad/scratch.pad.rb +0 -224
- data/lib/store-commands.txt +0 -180
data/lib/exception/cli.error.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
|
3
|
-
module OpenError
|
4
|
-
|
5
|
-
# This class is the parent to all opensession errors
|
6
|
-
# that originate from the command line.
|
7
|
-
#
|
8
|
-
# All opensession cli originating errors are about
|
9
|
-
#
|
10
|
-
# - a problem with the input or
|
11
|
-
# - a problem with the current state or
|
12
|
-
# - a predictable future problem
|
13
|
-
class CliError < StandardError
|
14
|
-
|
15
|
-
|
16
|
-
# Initialize the error and provide a culprit
|
17
|
-
# object which will be to-stringed and given
|
18
|
-
# out as evidence (look at this)!
|
19
|
-
#
|
20
|
-
# This method will take care of loggin the error.
|
21
|
-
#
|
22
|
-
# @param message [String] human readable error message
|
23
|
-
# @param culprit [Object] object that is either pertinent, a culprit or culpable
|
24
|
-
def initialize message, culprit
|
25
|
-
|
26
|
-
super(message)
|
27
|
-
|
28
|
-
@the_culprit = culprit
|
29
|
-
|
30
|
-
log.info(x) { "An [Error] Occured => #{message}" }
|
31
|
-
log.info(x) { "Object of Interest => #{culprit.to_s}" } unless culprit.nil?
|
32
|
-
log.info(x) { "Class Name Culprit => #{culprit.class.name}" }
|
33
|
-
log.info(x) { "Error Message From => #{self.class.name}" }
|
34
|
-
e.backtrace.to_s.log_lines
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
|
39
|
-
# This method gives interested parties the object that
|
40
|
-
# is at the centre of the exception. This object is either
|
41
|
-
# very pertinent, culpable or at the very least, interesting.
|
42
|
-
#
|
43
|
-
# @return [String] string representation of culpable object
|
44
|
-
def culprit
|
45
|
-
return "No culprit identified." if @the_culprit.nil?
|
46
|
-
return @the_culprit.to_s
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
|
3
|
-
# XXXXXXXXXXXXXXXXXXXX is this printed by yard
|
4
|
-
# XXXXXXXXXXXXXXXXXXXX is this printed by yard
|
5
|
-
# XXXXXXXXXXXXXXXXXXXX is this printed by yard
|
6
|
-
# XXXXXXXXXXXXXXXXXXXX is this printed by yard
|
7
|
-
# @note are modules documented by yard
|
8
|
-
# or are they simply ignored.
|
9
|
-
module OpenError
|
10
|
-
|
11
|
-
=begin
|
12
|
-
# Throw this error if the configured safe directory points to a file.
|
13
|
-
class SafeDirectoryIsFile < OpenError::CliError; end;
|
14
|
-
|
15
|
-
# Throw this error if safe directory path is either nil or empty.
|
16
|
-
class SafeDirNotConfigured < OpenError::CliError; end;
|
17
|
-
|
18
|
-
# Throw this error if the email address is nil, empty or less than 5 characters.
|
19
|
-
class EmailAddrNotConfigured < OpenError::CliError; end;
|
20
|
-
|
21
|
-
# Throw this error if the store url is either nil or empty.
|
22
|
-
class StoreUrlNotConfigured < OpenError::CliError; end;
|
23
|
-
|
24
|
-
# Throw if "prime folder" name occurs 2 or more times in the path.
|
25
|
-
class SafePrimeNameRepeated < OpenError::CliError; end;
|
26
|
-
|
27
|
-
# Throw if "prime folder" name occurs 2 or more times in the path.
|
28
|
-
class SafePrimeNameNotAtEnd < OpenError::CliError; end;
|
29
|
-
=end
|
30
|
-
|
31
|
-
end
|
data/lib/interprete/begin.rb
DELETED
@@ -1,232 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
|
3
|
-
module OpenSecret
|
4
|
-
|
5
|
-
require 'openssl'
|
6
|
-
|
7
|
-
# Collecting and immediately <b>locking up the master password</b> is
|
8
|
-
# the sole purpose of the <tt>begin use case</tt>.
|
9
|
-
#
|
10
|
-
# Under certain conditions this {Begin} use case takes the human (key)
|
11
|
-
# password and transforms it using a powerful message digester and then
|
12
|
-
# uses the decrypted form of the <b>OPS_KEY environment variable</b> to
|
13
|
-
# securely encrypt the generated key.
|
14
|
-
#
|
15
|
-
# This use case then writes (or overwrites) into the workstation configuration
|
16
|
-
# under the domain three (3) key/value pairs which are
|
17
|
-
#
|
18
|
-
# - the parent shell id encrypted using the decrypted OPS_KEY form
|
19
|
-
# - the time stamp that will be used as a nonce by future cases
|
20
|
-
# - the generated digested hash encrypted with the decrypted OPS_KEY
|
21
|
-
#
|
22
|
-
# Then the use case prints a success message and exits.
|
23
|
-
#
|
24
|
-
# <b>Laziness | The 4 Conditions Instigating Action</b>
|
25
|
-
#
|
26
|
-
# No state is changed unless 3 conditions are met. We take action only if
|
27
|
-
# all four of the below conditions are true.
|
28
|
-
#
|
29
|
-
# - the encrypted private key is present under the domain's front end drive
|
30
|
-
# - the OPS_KEY is present - Otherwise we report the error and then EXIT
|
31
|
-
# - the decrypted PPID is not present or does not match - Otherwise all is good
|
32
|
-
# - the password is found in at least 1 of 7 places - Otherwise we error EXIT
|
33
|
-
#
|
34
|
-
# If all four conditions are true then we take action and ultimately write
|
35
|
-
# (or overwrite) the 3 key/value pairs stated above into the domain section of
|
36
|
-
# the workstation's configuration file.
|
37
|
-
#
|
38
|
-
# == OPS_KEY environment variable | Pre-Condition
|
39
|
-
#
|
40
|
-
# We cannot securely lock the master password without the uncrackable
|
41
|
-
# <b>48 character session key</b> encrypted so that it is only accessible
|
42
|
-
# by the single shell that opensecret is being called from.
|
43
|
-
#
|
44
|
-
# Therefore it is a pre-requisite that the session key has been created
|
45
|
-
# and locked down by the {key} use case using the below shell command.
|
46
|
-
#
|
47
|
-
# $ export OPS_KEY=`ops key` # Export the generated session key
|
48
|
-
# $ env | grep OPS_KEY # Check that OPS_KEY indeed exists
|
49
|
-
#
|
50
|
-
# Note the <b>back-ticks</b> surrounding the <tt>ops key</tt> call.
|
51
|
-
#
|
52
|
-
# == Where Does {Begin} Fit?
|
53
|
-
#
|
54
|
-
# The <tt>ops begin</tt> use case is called near the beginning of EVERY
|
55
|
-
# opensecret command session.
|
56
|
-
#
|
57
|
-
# <b>Called Before (once per domain)</b>
|
58
|
-
#
|
59
|
-
# $ ops init <<domain_name>>, <<drive_path>>
|
60
|
-
#
|
61
|
-
# <b>Called Before (once per session)</b>
|
62
|
-
#
|
63
|
-
# At the beginning of every sitting (session) we must create a session key.
|
64
|
-
#
|
65
|
-
# $ export OPS_KEY=`ops key` # Export the generated session key
|
66
|
-
# $ ops begin # Kicks off the opensecret session
|
67
|
-
#
|
68
|
-
# The password will then be retrieved safely via a prompt. Note there are
|
69
|
-
# several other ways to deliver a password securely and/or via non-human
|
70
|
-
# actors <b>like scripts</b>.
|
71
|
-
#
|
72
|
-
# <b>Called After</b>
|
73
|
-
#
|
74
|
-
# A whole plethora of commands such as open, put, seal, post, reopen, read
|
75
|
-
# write, import and export.
|
76
|
-
#
|
77
|
-
# The {end} use case safely cleans up and tears down session keys and data.
|
78
|
-
#
|
79
|
-
# $ ops end
|
80
|
-
#
|
81
|
-
# When the shell closes OPS_KEY disappears forever. However if you want to
|
82
|
-
# continue using the same shell you can wipe this away.
|
83
|
-
#
|
84
|
-
# $ unset OPS_KEY # Delete the shell session key
|
85
|
-
# $ env | grep OPS_KEY # Check OPS_KEY is deleted
|
86
|
-
#
|
87
|
-
# You can also delete every env var created by this shell.
|
88
|
-
#
|
89
|
-
# $ env -i bash # Rewind to (after) login variables
|
90
|
-
#
|
91
|
-
#
|
92
|
-
# <b>The 7 Ways to Communicate a Password</b>
|
93
|
-
#
|
94
|
-
# Soon, seven secure methods of password delivery will be implemented and
|
95
|
-
# will include
|
96
|
-
#
|
97
|
-
# - collection from an environment varialbe set before execution
|
98
|
-
# - collection from a (possibly encrypted) local file
|
99
|
-
# - collection key-value stores such as Redis, etcd and Cassandra
|
100
|
-
# - collection via secure (https) including to tokenized S3 urls
|
101
|
-
#
|
102
|
-
class Begin < Command
|
103
|
-
|
104
|
-
attr_writer :outer_path, :master_p4ss, :domain_name
|
105
|
-
|
106
|
-
|
107
|
-
# If <b>4 conditions are met</b> this {Begin} use case takes the human (key)
|
108
|
-
# password and transforms it using a powerful message digester and then
|
109
|
-
# uses the decrypted form of the <b>OPS_KEY environment variable</b> to
|
110
|
-
# securely encrypt the generated key.
|
111
|
-
#
|
112
|
-
# This use case then writes (or overwrites) into the workstation configuration
|
113
|
-
# under the domain three (3) key/value pairs which are
|
114
|
-
#
|
115
|
-
# - the parent shell id encrypted using the decrypted OPS_KEY form
|
116
|
-
# - the time stamp that will be used as a nonce by future cases
|
117
|
-
# - the generated digested hash encrypted with the decrypted OPS_KEY
|
118
|
-
#
|
119
|
-
# Then the use case prints a success message and exits.
|
120
|
-
#
|
121
|
-
# <b>Laziness | The 4 Conditions Instigating Action</b>
|
122
|
-
#
|
123
|
-
# No state is changed unless 3 conditions are met. We take action only if
|
124
|
-
# all four of the below conditions are true.
|
125
|
-
#
|
126
|
-
# - the encrypted private key is present under the domain's front end drive
|
127
|
-
# - the OPS_KEY is present - Otherwise we report the error and then EXIT
|
128
|
-
# - the decrypted PPID is not present or does not match - Otherwise all is good
|
129
|
-
# - the password is found in at least 1 of 7 places - Otherwise we error EXIT
|
130
|
-
#
|
131
|
-
# If all four conditions are true then we take action and ultimately write
|
132
|
-
# (or overwrite) the 3 key/value pairs stated above into the domain section of
|
133
|
-
# the workstation's configuration file.
|
134
|
-
#
|
135
|
-
#
|
136
|
-
# <b>The 7 Ways to Communicate a Password</b>
|
137
|
-
#
|
138
|
-
# Soon, seven secure methods of password delivery will be implemented and
|
139
|
-
# will include
|
140
|
-
#
|
141
|
-
# - collection from an environment varialbe set before execution
|
142
|
-
# - collection from a (possibly encrypted) local file
|
143
|
-
# - collection key-value stores such as Redis, etcd and Cassandra
|
144
|
-
# - collection via secure (https) including to tokenized S3 urls
|
145
|
-
#
|
146
|
-
# <b>Begin | Observable Value</b>
|
147
|
-
#
|
148
|
-
# There are three valuable state changes enacted by this use case.
|
149
|
-
#
|
150
|
-
# - the <b>password for the domain is collected</b> (in one of several ways)
|
151
|
-
# - the <b>session key</b> is acquired from crypted OPS_KEY environment variable
|
152
|
-
# - an <b>encrypted password</b> results from locking password with session key
|
153
|
-
# - the <b>encrypted password</b> is put into the domain's configuration keystore
|
154
|
-
# - a <b>timestamped session folder</b> is created to harbour session artifacts
|
155
|
-
#
|
156
|
-
# <b>Summary </b>
|
157
|
-
#
|
158
|
-
# Observable value is the opensecret domain password locked with a robust
|
159
|
-
# symmetric encryption key that is at least 48 characters in length and
|
160
|
-
# placed into the domain's keystore configuration file.
|
161
|
-
def execute
|
162
|
-
|
163
|
-
hash_dict = OpenKey::Dictionary.create_with_section "/home/apollo/.opensecret.io/tmp.backing.file.txt", "this.section"
|
164
|
-
OpenKey::Key256.generate( "human_secret", hash_dict )
|
165
|
-
|
166
|
-
exit
|
167
|
-
|
168
|
-
for n in 0 .. 63
|
169
|
-
|
170
|
-
bcrypt_key = OpenKey::BCryptKeyGen.new.generate_key("human_secret")
|
171
|
-
bcrypt_len = bcrypt_key.to_s.length
|
172
|
-
pbkdf2_key = OpenKey::Pbkdf2KeyGen.new.generate_key("human_secret")
|
173
|
-
pbkdf2_len = pbkdf2_key.to_s.length
|
174
|
-
|
175
|
-
index = "%02d" % [ n.to_s ]
|
176
|
-
|
177
|
-
puts "---------------------------------------------------------------------"
|
178
|
-
puts "Calculation Number [#{index}]"
|
179
|
-
puts "---------------------------------------------------------------------"
|
180
|
-
puts "Len #{bcrypt_key.to_oc64.length} => #{bcrypt_key.to_oc64}"
|
181
|
-
puts "Len #{bcrypt_len} => #{bcrypt_key.to_s}"
|
182
|
-
puts "Len #{pbkdf2_key.to_oc64.length} => #{pbkdf2_key.to_oc64}"
|
183
|
-
puts "Len #{pbkdf2_len} => #{pbkdf2_key.to_s}"
|
184
|
-
|
185
|
-
end
|
186
|
-
|
187
|
-
exit
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
return unless ops_key_exists?
|
192
|
-
|
193
|
-
instantiate_collateral
|
194
|
-
@domain_name = @collateral.domain_name
|
195
|
-
|
196
|
-
return unless private_key_exists?
|
197
|
-
|
198
|
-
lock_stored = Mapper::Settings.contains_key?( session_id, MASTER_LOCK_KEY_NAME )
|
199
|
-
print_success_initializing if lock_stored
|
200
|
-
return if lock_stored
|
201
|
-
|
202
|
-
unless @master_p4ss
|
203
|
-
@master_p4ss = ToolBelt::Collect.secret_text(
|
204
|
-
@c[:global][:min_passwd_len],
|
205
|
-
false,
|
206
|
-
"Enter Ops Password "
|
207
|
-
)
|
208
|
-
end
|
209
|
-
|
210
|
-
create_crypt_store_locking_key
|
211
|
-
print_success_initializing
|
212
|
-
|
213
|
-
end
|
214
|
-
|
215
|
-
|
216
|
-
# Perform pre-conditional validations in preparation to executing the main flow
|
217
|
-
# of events for this use case. This method may throw the below exceptions.
|
218
|
-
#
|
219
|
-
# @raise [SafeDirNotConfigured] if the safe's url has not been configured
|
220
|
-
# @raise [EmailAddrNotConfigured] if the email address has not been configured
|
221
|
-
# @raise [StoreUrlNotConfigured] if the crypt store url is not configured
|
222
|
-
def pre_validation
|
223
|
-
|
224
|
-
end
|
225
|
-
|
226
|
-
|
227
|
-
end
|
228
|
-
|
229
|
-
|
230
|
-
end
|
231
|
-
|
232
|
-
|
data/lib/interprete/cmd.rb
DELETED
@@ -1,621 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby
|
2
|
-
# coding: utf-8
|
3
|
-
|
4
|
-
module OpenSecret
|
5
|
-
|
6
|
-
# The parent OpenSecret use case is designed to be extended by the cli
|
7
|
-
# (command line) use cases like {OpenSecret::Open}, {OpenSecret::Put} and
|
8
|
-
# {OpenSecret::Lock} because it describes behaviour common to at least two
|
9
|
-
# (but usually more) of the use cases.
|
10
|
-
#
|
11
|
-
# == Common Use Case Behaviour
|
12
|
-
#
|
13
|
-
# This {OpenSecret::Command} use case is designed to be extended and does preparatory
|
14
|
-
# work to create favourable and useful conditions to make use cases readable,
|
15
|
-
# less repetitive, simpler and concise.
|
16
|
-
class Command
|
17
|
-
|
18
|
-
|
19
|
-
# Get the envelope that <b>was opened</b> by the open command but
|
20
|
-
# <b>not locked</b> with the lock command.
|
21
|
-
#
|
22
|
-
# @return [Envelope]
|
23
|
-
# return the Envelope that has been opened. The state carried alongside an
|
24
|
-
# open envelope is an id, an encryption key and a filepath all inside the
|
25
|
-
# userhome configuration file.
|
26
|
-
def get_envelope
|
27
|
-
|
28
|
-
encrypt_key = Mapper::Settings.read @c[:open][:open_name], @c[:open][:open_keyname]
|
29
|
-
rel_filepath = Mapper::Settings.read @c[:open][:open_name], @c[:open][:open_pathname]
|
30
|
-
|
31
|
-
put_filepath = File.join( Mapper::Collateral.instance.session_envelopes_path, rel_filepath )
|
32
|
-
the_envelope = Mapper::Envelope.new
|
33
|
-
the_envelope.read put_filepath, encrypt_key
|
34
|
-
return the_envelope
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
|
39
|
-
# This method uses a one-way function to return a combinatorial digested
|
40
|
-
# session identification string using a number of distinct parameters that
|
41
|
-
# deliver the important behaviours of changing in certain circumstances
|
42
|
-
# and remaining unchanged in others.
|
43
|
-
#
|
44
|
-
# <b>Change | When Should the Session ID Change?</b>
|
45
|
-
#
|
46
|
-
# The session id is not a secret but it has to be unique due to its role
|
47
|
-
# in indexing the session envelopes and their changes.
|
48
|
-
#
|
49
|
-
# What is really important is that the <b>session id changes</b> when
|
50
|
-
#
|
51
|
-
# - the <b>domain</b> being used changes
|
52
|
-
# - the <b>command shell</b> changes
|
53
|
-
# - the user <b>switches to another workstation user</b>
|
54
|
-
# - the <b>workstation host</b> is changed
|
55
|
-
# - the <b>OPS KEY</b> environment variable changes
|
56
|
-
# - the user <b>SSH's</b> into another shell
|
57
|
-
#
|
58
|
-
# A distinct workstation is identified by the first MAC address and the
|
59
|
-
# hostname of the machine.
|
60
|
-
#
|
61
|
-
# <b>Unchanged | When Should it Remain Unchanged?</b>
|
62
|
-
#
|
63
|
-
# Remaining <b>unchanged</b> in certain scenarious is a session ID feature
|
64
|
-
# that is just as important as it changing in others.
|
65
|
-
#
|
66
|
-
# The session ID <b>must remain unchanged</b> when
|
67
|
-
#
|
68
|
-
# - the <b>user returns to a command shell</b>
|
69
|
-
# - the user <b>switches back to using a domain</b>
|
70
|
-
# - the user exits their <b>remote SSH session</b>
|
71
|
-
# - <b>sudo is used</b> to execute the commands
|
72
|
-
# - the user comes back to their <b>workstation</b>
|
73
|
-
# - the clock ticks into another day, month, year ...
|
74
|
-
#
|
75
|
-
# The pre-hash inputs are an amalgam of the below example data.
|
76
|
-
#
|
77
|
-
# Mac Address => 20cf3067dec3
|
78
|
-
# Parent PID => 5817
|
79
|
-
# Machine Host => data-cruncher
|
80
|
-
# Session Time => 18083.0310.41.796577366
|
81
|
-
#
|
82
|
-
# @return [String]
|
83
|
-
# the session id
|
84
|
-
def session_id
|
85
|
-
|
86
|
-
require 'macaddr'
|
87
|
-
session_data_points = [
|
88
|
-
Mac.addr.to_alphanumeric,
|
89
|
-
Process.ppid.to_s,
|
90
|
-
Socket.gethostname,
|
91
|
-
ENV[ Mapper::Collateral::ENV_OPS_KEY_NAME ].strip,
|
92
|
-
OpenSession::Home.instance.username,
|
93
|
-
@domain_name
|
94
|
-
].join
|
95
|
-
|
96
|
-
return OpenKey::Digester.mash( session_data_points, SESSION_ID_SIZE )
|
97
|
-
|
98
|
-
end
|
99
|
-
|
100
|
-
|
101
|
-
# Get the session key that is used for symmetric encryption and decryption
|
102
|
-
# of secret material pertinent to the opensecret use case.
|
103
|
-
#
|
104
|
-
# This depends on the {Command.session_key_password} for engineering the
|
105
|
-
# password using information specific to the workstation and the parent
|
106
|
-
# (bash) shell being used to execute opensecret.
|
107
|
-
#
|
108
|
-
# <b>Substituting Equals Back In</b>
|
109
|
-
#
|
110
|
-
# An environment variable holds the key's value and the <b>equals sign</b>
|
111
|
-
# is not allowed within environment variables (in some operating systems)
|
112
|
-
# mainly due to the fact that it is used as the key/value separator.
|
113
|
-
#
|
114
|
-
# The url safe base64 coding may produce one, two or three equal signs in
|
115
|
-
# a bid to pad the string into a multiple of 4 length.
|
116
|
-
#
|
117
|
-
# In this case, we replaced equals signs with @ symbols when encoding so
|
118
|
-
# we must reciprocate this action when decoding here.
|
119
|
-
#
|
120
|
-
# @return [String]
|
121
|
-
# Return the original key generated by the {OpenSecret::Key} usecase.
|
122
|
-
#
|
123
|
-
# @raise [RuntimeError]
|
124
|
-
# if either the ENV["OPS_KEY"] environment variable is not present or an
|
125
|
-
# exception is thrown during decryption. An error is also thrown if the
|
126
|
-
# environment variable or the key to be returned have a length less than
|
127
|
-
# 40 characters.
|
128
|
-
def get_session_key
|
129
|
-
|
130
|
-
key_ciphertext = mandatory_environment_variable Mapper::Collateral::ENV_OPS_KEY_NAME, 40
|
131
|
-
decoded_crypt = Base64.urlsafe_decode64( key_ciphertext.gsub("@","=") )
|
132
|
-
return ToolBelt::Blowfish.decryptor( decoded_crypt, session_key_password )
|
133
|
-
|
134
|
-
end
|
135
|
-
|
136
|
-
|
137
|
-
# @todo
|
138
|
-
# this method should be within the env var services class.
|
139
|
-
#
|
140
|
-
# Return the contents of the environment variable key specified
|
141
|
-
# in the first parameter. The returned value is striped of both
|
142
|
-
# leading and trailing whitespace before it is returned.
|
143
|
-
#
|
144
|
-
# @param env_var_name [String]
|
145
|
-
# the (uppercase) name of the environment variable to collect
|
146
|
-
#
|
147
|
-
# @param min_size [Number]
|
148
|
-
# the minimum number of characters the environment variable's
|
149
|
-
# value is allowed to contain.
|
150
|
-
#
|
151
|
-
# @return [String]
|
152
|
-
# Return the <b>stripped</b> environment variable value whose
|
153
|
-
# name matches the one specified by the first parameter.
|
154
|
-
#
|
155
|
-
# @raise [RuntimeError]
|
156
|
-
# if either the environment variable is not present or the length
|
157
|
-
# of the string value is less than the number of charactes
|
158
|
-
# specified in the second parameterr.
|
159
|
-
def mandatory_environment_variable env_var_name, min_size
|
160
|
-
|
161
|
-
raw_env_var_value = ENV[env_var_name]
|
162
|
-
raise_error( env_var_name, "not present") unless raw_env_var_value
|
163
|
-
|
164
|
-
env_var_value = raw_env_var_value.strip
|
165
|
-
raise_error( env_var_name, "consists only of whitespace") if raw_env_var_value.empty?
|
166
|
-
|
167
|
-
size_msg = "length should be at [least] #{min_size} characters"
|
168
|
-
raise_error( env_var_name, size_msg ) unless env_var_value.length >= min_size
|
169
|
-
|
170
|
-
return env_var_value
|
171
|
-
|
172
|
-
end
|
173
|
-
|
174
|
-
|
175
|
-
# Instantiate the collateral object which is the single
|
176
|
-
# source of knowledge for any client wishing to know the
|
177
|
-
# path to a particular resource (collateral) file or
|
178
|
-
# directory on the accessible file system.
|
179
|
-
#
|
180
|
-
# This method simply enables, for all (child) use cases,
|
181
|
-
# the @collateral instance variable.
|
182
|
-
def instantiate_collateral
|
183
|
-
|
184
|
-
@collateral = Mapper::Collateral.instance
|
185
|
-
|
186
|
-
the_domain_name = Mapper::Settings.grab @c[:global][:domain_now_id]
|
187
|
-
the_frontend_path = Mapper::Settings.read( the_domain_name, @c[:global][:front_path_id] )
|
188
|
-
@collateral.domain_name = the_domain_name
|
189
|
-
@collateral.frontend_path = the_frontend_path
|
190
|
-
|
191
|
-
end
|
192
|
-
|
193
|
-
|
194
|
-
# Unlock the {OpenSSL::PKey::RSA} private key and return the cipher object
|
195
|
-
# usable for asymmetric encryption, decryption, signing and signature
|
196
|
-
# verification use cases.
|
197
|
-
#
|
198
|
-
# The returned private key can be used to generate its twin public key
|
199
|
-
# and should be used to verify the same public key as (if) and when the
|
200
|
-
# need arises.
|
201
|
-
#
|
202
|
-
# @param locked_private_key [String]
|
203
|
-
# the locked up private key ciphertext
|
204
|
-
#
|
205
|
-
# @param unlock_key [String]
|
206
|
-
# the symmetric encryption key that can be used to unlock the private
|
207
|
-
# key ciphertext in the first parameter.
|
208
|
-
#
|
209
|
-
# @return [OpenSSL::PKey::RSA]
|
210
|
-
# return the {OpenSSL::PKey::RSA} private key that will be
|
211
|
-
# usable for asymmetric encryption, decryption, signing and signature
|
212
|
-
# verification.
|
213
|
-
def unlock_private_key locked_private_key, unlock_key
|
214
|
-
return OpenSSL::PKey::RSA.new locked_private_key, unlock_key
|
215
|
-
end
|
216
|
-
|
217
|
-
|
218
|
-
# Return the {OpenSSL::PKey::RSA} private key which is a cipher object
|
219
|
-
# usable for asymmetric encryption, decryption, signing and signature
|
220
|
-
# verification use cases.
|
221
|
-
#
|
222
|
-
# The returned private key can be used to generate its twin public key
|
223
|
-
# and should be used to verify the same public key as (if) and when the
|
224
|
-
# need arises.
|
225
|
-
#
|
226
|
-
# @param private_key_text [String]
|
227
|
-
# the private key plain text
|
228
|
-
#
|
229
|
-
# @return [OpenSSL::PKey::RSA]
|
230
|
-
# return the {OpenSSL::PKey::RSA} private key that will be
|
231
|
-
# usable for asymmetric encryption, decryption, signing and signature
|
232
|
-
# verification.
|
233
|
-
def to_private_key private_key_text
|
234
|
-
return OpenSSL::PKey::RSA.new private_key_text
|
235
|
-
end
|
236
|
-
|
237
|
-
|
238
|
-
# Execute the use cases's flow from beginning when
|
239
|
-
# you validate the input and parameters through the
|
240
|
-
# memorize, execute and the final cleanup.
|
241
|
-
def flow_of_events
|
242
|
-
|
243
|
-
check_pre_conditions
|
244
|
-
pre_memorize
|
245
|
-
execute
|
246
|
-
post_memorize
|
247
|
-
cleanup
|
248
|
-
check_post_conditions
|
249
|
-
|
250
|
-
end
|
251
|
-
|
252
|
-
|
253
|
-
# Validate the input parameters and check that the current
|
254
|
-
# state is perfect for executing the use case.
|
255
|
-
#
|
256
|
-
# If either of the above fail - the validation function should
|
257
|
-
# set a human readable string and then throw an exception.
|
258
|
-
def check_pre_conditions
|
259
|
-
|
260
|
-
|
261
|
-
begin
|
262
|
-
|
263
|
-
pre_validation
|
264
|
-
|
265
|
-
rescue OpenError::CliError => e
|
266
|
-
|
267
|
-
puts ""
|
268
|
-
puts "Your command did not complete successfully."
|
269
|
-
puts "Pre validation checks failed."
|
270
|
-
puts ""
|
271
|
-
puts " => #{e.message}"
|
272
|
-
puts ""
|
273
|
-
abort e.message
|
274
|
-
end
|
275
|
-
|
276
|
-
|
277
|
-
end
|
278
|
-
|
279
|
-
|
280
|
-
# After the main flow of events certain state conditions
|
281
|
-
# must hold true thus demonstrating that the observable
|
282
|
-
# value has indeed ben delivered.
|
283
|
-
#
|
284
|
-
# Child classes should subclass this method and place any
|
285
|
-
# post execution (post condition) checks in it and then
|
286
|
-
# make a call to this method through the "super" keyword.
|
287
|
-
def check_post_conditions
|
288
|
-
|
289
|
-
begin
|
290
|
-
|
291
|
-
post_validation
|
292
|
-
|
293
|
-
rescue OpenError::CliError => e
|
294
|
-
|
295
|
-
puts ""
|
296
|
-
puts "Your command did not complete successfully."
|
297
|
-
puts "Post validation checks failed."
|
298
|
-
puts ""
|
299
|
-
puts " => #{e.message}"
|
300
|
-
#### puts " => #{e.culprit}"
|
301
|
-
puts ""
|
302
|
-
abort e.message
|
303
|
-
end
|
304
|
-
|
305
|
-
end
|
306
|
-
|
307
|
-
|
308
|
-
# Child classes should subclass this method and place any
|
309
|
-
# post execution (post condition) checks in it and then
|
310
|
-
# make a call to this method through the "super" keyword if
|
311
|
-
# this method gets any global behaviour in it worth calling.
|
312
|
-
def post_validation
|
313
|
-
|
314
|
-
end
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
# (Pre) memorize adds to permanent storage a configuration
|
319
|
-
# directive and its value <tt>provided by the user</tt>
|
320
|
-
# primarily because it avoids repitition in subsequent
|
321
|
-
# use case commands.
|
322
|
-
def pre_memorize
|
323
|
-
|
324
|
-
end
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
# Execute the main flow of events of the use case. Any
|
329
|
-
# exceptions thrown are captured and if the instance
|
330
|
-
# variale [@human_readable_message] is set - tell the
|
331
|
-
# user about it. Without any message - just tell the
|
332
|
-
# user something went wrong and tell them where the logs
|
333
|
-
# are that might carry more information.
|
334
|
-
def execute
|
335
|
-
|
336
|
-
end
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
# (Post) memorize will add to permanent storage) any
|
341
|
-
# key/values that have been derived or looked up during
|
342
|
-
# use case execution. This allows subsequent use case
|
343
|
-
# commands to
|
344
|
-
#
|
345
|
-
# - capitalize on
|
346
|
-
# - take direction from
|
347
|
-
# - and/or simply use
|
348
|
-
#
|
349
|
-
# the value in later use cases or processing.
|
350
|
-
def post_memorize
|
351
|
-
|
352
|
-
end
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
# If the use case validation went well, the memorization
|
357
|
-
# went well the
|
358
|
-
def cleanup
|
359
|
-
|
360
|
-
end
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
# Resolve +two attached fact files+ with the first being general and
|
365
|
-
# relevant to every use case in the parameter specified domain, and
|
366
|
-
# the second being specific to this use case.
|
367
|
-
#
|
368
|
-
# The <b><em>general factfile</em></b> is expected to be on the class (load) path
|
369
|
-
# and below are paths for a generic domain name and for an example domain
|
370
|
-
# name of *openpost.io*
|
371
|
-
#
|
372
|
-
# - <tt>factbase/facts.<<domain>>.ini</tt>
|
373
|
-
# - <tt>factbase/facts.openpost.io.ini</tt>
|
374
|
-
#
|
375
|
-
# The <b><em>use case specific factfile</em></b> is also _expected_ to be on the class
|
376
|
-
# (load) path and below is a generic and a use case specific path using
|
377
|
-
# the example <tt>deliver</tt> use case.
|
378
|
-
#
|
379
|
-
# - <tt>factbase/facts.<<uc-name>>.usecase.ini</tt>
|
380
|
-
# - <tt>factbase/facts.deliver.usecase.ini</tt>
|
381
|
-
#
|
382
|
-
# The domain name is delivered in the parameter whilst the use case
|
383
|
-
# name is derived from the (extension) class name.
|
384
|
-
#
|
385
|
-
# @param fact_domain [String] domain of the general attached factfile
|
386
|
-
# @return [Hash] a 2d (two-dimensional) hash of keys and their corresponding values
|
387
|
-
def attached_facts fact_domain
|
388
|
-
|
389
|
-
end
|
390
|
-
|
391
|
-
|
392
|
-
# This use case is initialized primary by resolving the configured
|
393
|
-
# +general and use case specific facts+. To access the general facts,
|
394
|
-
# a domain name is expected in the parameter delegated by the extension
|
395
|
-
# use case classes.
|
396
|
-
def initialize
|
397
|
-
|
398
|
-
|
399
|
-
class_name = self.class.name.downcase.split(":").last
|
400
|
-
is_pre_init_usecase = [ "safe", "store", "email" ].include? class_name
|
401
|
-
return if is_pre_init_usecase
|
402
|
-
|
403
|
-
@ucid_str = self.class.name.do_flatten
|
404
|
-
log.info(x) { "Usecase class [self.class.name] converted to => #{@ucid_str}" }
|
405
|
-
@ucid_sym = @ucid_str.gsub(".", "_").to_sym
|
406
|
-
|
407
|
-
OpenSession::FactFind.instance.instantiate @ucid_str
|
408
|
-
OpenSession::FactFind.instance.assimilate "facts.opensecret.io.ini"
|
409
|
-
|
410
|
-
@c = OpenSession::FactFind.instance.f
|
411
|
-
@i = OpenSession::FactFind.instance.i
|
412
|
-
@p = OpenSession::FactFind.instance.f[@ucid_sym]
|
413
|
-
|
414
|
-
log.info(x) { "assimilated [#{@p.length}] facts specific to the [#{@ucid_str}] use case." }
|
415
|
-
|
416
|
-
end
|
417
|
-
|
418
|
-
|
419
|
-
private
|
420
|
-
|
421
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
422
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
423
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
424
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
425
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
426
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
427
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
428
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
429
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
430
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
431
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
432
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
433
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
434
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
435
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
436
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
437
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
438
|
-
## Important Refactor is to move Session behaviour to an OpenKey Session class.
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
OUTER_PATH = "outer.path"
|
443
|
-
INNER_PATH = "inner.path"
|
444
|
-
|
445
|
-
LAST_ACCESSED = "last.accessed.time"
|
446
|
-
|
447
|
-
SESSION_DICT_LOCK_SIZE = 32
|
448
|
-
|
449
|
-
SESSION_DICT_LOCK_NAME = "crypted.session.dict.lock"
|
450
|
-
|
451
|
-
ENVELOPE_KEY_SIZE = 32
|
452
|
-
|
453
|
-
ENVELOPE_KEY_NAME = "crypted.envelope.key"
|
454
|
-
|
455
|
-
ENVELOPE_ID_SIZE = 16
|
456
|
-
|
457
|
-
ENVELOPE_ID_NAME = "crypted.envelope.id"
|
458
|
-
|
459
|
-
SESSION_ID_SIZE = 64
|
460
|
-
|
461
|
-
SESSION_FILENAME_ID_SIZE = 24
|
462
|
-
|
463
|
-
SESSION_START_TIMESTAMP_NAME = "session.creation.time"
|
464
|
-
|
465
|
-
MASTER_LOCK_KEY_NAME = "master.session.lock.key"
|
466
|
-
|
467
|
-
def raise_error env_var_name, message
|
468
|
-
|
469
|
-
puts ""
|
470
|
-
puts "#{env_var_name} environment variable #{message}."
|
471
|
-
puts "To instantiate it you can use the below command."
|
472
|
-
puts ""
|
473
|
-
puts "$ export OPS_KEY=`ops key`"
|
474
|
-
puts ""
|
475
|
-
puts "ps => those are backticks around `ops key` (not apostrophes)."
|
476
|
-
puts ""
|
477
|
-
|
478
|
-
raise RuntimeError, "#{env_var_name} environment variable #{message}."
|
479
|
-
|
480
|
-
end
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
def ops_key_exists?
|
485
|
-
|
486
|
-
if ( ENV.has_key? Mapper::Collateral::ENV_OPS_KEY_NAME )
|
487
|
-
return true
|
488
|
-
end
|
489
|
-
|
490
|
-
puts ""
|
491
|
-
puts "opensecret needs you to create a session key."
|
492
|
-
puts "To automate this step see the documentation."
|
493
|
-
puts "To create the key run the below command."
|
494
|
-
puts ""
|
495
|
-
puts " export OPS_KEY=`ops key`"
|
496
|
-
puts ""
|
497
|
-
puts "Those are backticks surrounding `ops key`"
|
498
|
-
puts "Not apostrophes."
|
499
|
-
puts ""
|
500
|
-
|
501
|
-
return false
|
502
|
-
|
503
|
-
end
|
504
|
-
|
505
|
-
|
506
|
-
def private_key_exists?
|
507
|
-
|
508
|
-
if @collateral.private_key_exists?
|
509
|
-
return true
|
510
|
-
end
|
511
|
-
|
512
|
-
puts ""
|
513
|
-
puts "Domain [ #{@domain_name} ] has not been initialized."
|
514
|
-
puts "Have you run the ops init command?"
|
515
|
-
puts ""
|
516
|
-
puts " ops init #{@domain_name} $HOME/ops.#{@domain_name}"
|
517
|
-
puts ""
|
518
|
-
|
519
|
-
return false
|
520
|
-
|
521
|
-
end
|
522
|
-
|
523
|
-
|
524
|
-
def get_session_dictionary
|
525
|
-
|
526
|
-
session_index_filepath = @collateral.session_index_file( session_id[0 .. (SESSION_FILENAME_ID_SIZE-1)] )
|
527
|
-
|
528
|
-
unless File.file? session_index_filepath
|
529
|
-
return OpenKey::Dictionary.create_with_section( session_index_filepath, create_envelope_id )
|
530
|
-
end
|
531
|
-
|
532
|
-
return OpenKey::Dictionary.create_with_section( session_index_filepath, read_envelope_id, read_session_dict_lock )
|
533
|
-
|
534
|
-
end
|
535
|
-
|
536
|
-
|
537
|
-
def create_session_dict_lock
|
538
|
-
session_dict_lock = OpenSecret::ToolBelt::Engineer.strong_key SESSION_DICT_LOCK_SIZE
|
539
|
-
session_dict_lock_crypt = session_dict_lock.encrypt_url_encode( get_session_key )
|
540
|
-
Mapper::Settings.write session_id, SESSION_DICT_LOCK_NAME, session_dict_lock_crypt
|
541
|
-
return session_dict_lock
|
542
|
-
end
|
543
|
-
|
544
|
-
|
545
|
-
def read_session_dict_lock
|
546
|
-
session_dict_lock_crypt = Mapper::Settings.read session_id, SESSION_DICT_LOCK_NAME
|
547
|
-
return session_dict_lock_crypt.url_decode_decrypt( get_session_key )
|
548
|
-
end
|
549
|
-
|
550
|
-
|
551
|
-
def create_envelope_key
|
552
|
-
envelope_key = OpenSecret::ToolBelt::Engineer.strong_key ENVELOPE_KEY_SIZE
|
553
|
-
envelope_key_crypt = envelope_key.encrypt_url_encode( get_session_key )
|
554
|
-
Mapper::Settings.write session_id, ENVELOPE_KEY_NAME, envelope_key_crypt
|
555
|
-
return envelope_key
|
556
|
-
end
|
557
|
-
|
558
|
-
|
559
|
-
def read_envelope_key
|
560
|
-
envelope_key_crypt = Mapper::Settings.read session_id, ENVELOPE_KEY_NAME
|
561
|
-
return envelope_key_crypt.url_decode_decrypt( get_session_key )
|
562
|
-
end
|
563
|
-
|
564
|
-
|
565
|
-
def create_envelope_id
|
566
|
-
envelope_id = OpenSecret::ToolBelt::Engineer.strong_key ENVELOPE_ID_SIZE
|
567
|
-
envelope_id_crypt = envelope_id.encrypt_url_encode( get_session_key )
|
568
|
-
Mapper::Settings.write session_id, ENVELOPE_ID_NAME, envelope_id_crypt
|
569
|
-
return envelope_id
|
570
|
-
end
|
571
|
-
|
572
|
-
|
573
|
-
def read_envelope_id
|
574
|
-
envelope_id_crypt = Mapper::Settings.read session_id, ENVELOPE_ID_NAME
|
575
|
-
return envelope_id_crypt.url_decode_decrypt( get_session_key )
|
576
|
-
end
|
577
|
-
|
578
|
-
|
579
|
-
#### [cGWy7vU4WwxcTQ5X710kVQGWD5EujUco0WiaxyLrMFzXMdNhCAYAba3DXENKVz94]
|
580
|
-
#### master.session.lock.key = FY27lfm96o6KlIXHS3u8tsmh9oBJiS5IU-oUkn4CLCkVAsOx_E2Wkzm2-d3ZIHfhG35ik1ENjhHcBJO4JebYQ-TR-t7xI8AVKC8jS3Y40qUbeCjFHjGDbpJI0RNYEWdyr4iSMygI6FbH-iCg9V4awA==
|
581
|
-
#### session.creation.time = 18092.2238.43.302186561
|
582
|
-
#### crypted.envelope.id = iPamzaAD17vmOH72DLikbwadIMc5HW8IBS88mxUseks8il74JEXuLpJI0RNYEWdyr4iSMygI6FbH-iCg9V4awA==
|
583
|
-
#### crypted.session.dict.lock = iSbFR3ZFGWzkFEXHMByugL_09FJL3wVRnmgjxqsvd5ds8zpfeQT-7ERiAAst49gQr4iSMygI6FbH-iCg9V4awA==
|
584
|
-
|
585
|
-
def create_crypt_store_locking_key
|
586
|
-
|
587
|
-
config_keystore = Mapper::KeyStore.new( @collateral.domain_config_file, @domain_name )
|
588
|
-
digester = OpenKey::Digester.new( config_keystore.time_stamp )
|
589
|
-
|
590
|
-
locking_key = digester.generate @master_p4ss
|
591
|
-
config_keystore.set_init_vector( digester.encrypted_iv )
|
592
|
-
|
593
|
-
session_key_crypt = Base64.urlsafe_encode64( ToolBelt::Blowfish.encryptor( locking_key, get_session_key ) )
|
594
|
-
|
595
|
-
Mapper::Settings.write session_id, MASTER_LOCK_KEY_NAME, session_key_crypt
|
596
|
-
Mapper::Settings.write session_id, SESSION_START_TIMESTAMP_NAME, OpenSession::Stamp.yyjjj_hhmm_ss_nanosec
|
597
|
-
|
598
|
-
return locking_key
|
599
|
-
|
600
|
-
end
|
601
|
-
|
602
|
-
|
603
|
-
def print_success_initializing
|
604
|
-
|
605
|
-
puts ""
|
606
|
-
puts "Success - now open a secret envelope, put, then seal."
|
607
|
-
puts ""
|
608
|
-
puts " ops open aws.credentials:s3reader"
|
609
|
-
puts " ops put access_key ABCD1234"
|
610
|
-
puts " ops put secret_key FGHIJ56789"
|
611
|
-
puts " ops put region_key eu-central-1"
|
612
|
-
puts " ops seal"
|
613
|
-
puts ""
|
614
|
-
|
615
|
-
end
|
616
|
-
|
617
|
-
|
618
|
-
end
|
619
|
-
|
620
|
-
|
621
|
-
end
|