safedb 0.01.0001

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.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.yardopts +3 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE +21 -0
  6. data/README.md +793 -0
  7. data/Rakefile +16 -0
  8. data/bin/safe +5 -0
  9. data/lib/configs/README.md +58 -0
  10. data/lib/extension/array.rb +162 -0
  11. data/lib/extension/dir.rb +35 -0
  12. data/lib/extension/file.rb +123 -0
  13. data/lib/extension/hash.rb +33 -0
  14. data/lib/extension/string.rb +572 -0
  15. data/lib/factbase/facts.safedb.net.ini +38 -0
  16. data/lib/interprete.rb +462 -0
  17. data/lib/keytools/PRODUCE_RAND_SEQ_USING_DEV_URANDOM.txt +0 -0
  18. data/lib/keytools/kdf.api.rb +243 -0
  19. data/lib/keytools/kdf.bcrypt.rb +265 -0
  20. data/lib/keytools/kdf.pbkdf2.rb +262 -0
  21. data/lib/keytools/kdf.scrypt.rb +190 -0
  22. data/lib/keytools/key.64.rb +326 -0
  23. data/lib/keytools/key.algo.rb +109 -0
  24. data/lib/keytools/key.api.rb +1391 -0
  25. data/lib/keytools/key.db.rb +330 -0
  26. data/lib/keytools/key.docs.rb +195 -0
  27. data/lib/keytools/key.error.rb +110 -0
  28. data/lib/keytools/key.id.rb +271 -0
  29. data/lib/keytools/key.ident.rb +243 -0
  30. data/lib/keytools/key.iv.rb +107 -0
  31. data/lib/keytools/key.local.rb +259 -0
  32. data/lib/keytools/key.now.rb +402 -0
  33. data/lib/keytools/key.pair.rb +259 -0
  34. data/lib/keytools/key.pass.rb +120 -0
  35. data/lib/keytools/key.rb +585 -0
  36. data/lib/logging/gem.logging.rb +132 -0
  37. data/lib/modules/README.md +43 -0
  38. data/lib/modules/cryptology/aes-256.rb +154 -0
  39. data/lib/modules/cryptology/amalgam.rb +70 -0
  40. data/lib/modules/cryptology/blowfish.rb +130 -0
  41. data/lib/modules/cryptology/cipher.rb +207 -0
  42. data/lib/modules/cryptology/collect.rb +138 -0
  43. data/lib/modules/cryptology/crypt.io.rb +225 -0
  44. data/lib/modules/cryptology/engineer.rb +99 -0
  45. data/lib/modules/mappers/dictionary.rb +288 -0
  46. data/lib/modules/storage/coldstore.rb +186 -0
  47. data/lib/modules/storage/git.store.rb +399 -0
  48. data/lib/session/fact.finder.rb +334 -0
  49. data/lib/session/require.gem.rb +112 -0
  50. data/lib/session/time.stamp.rb +340 -0
  51. data/lib/session/user.home.rb +49 -0
  52. data/lib/usecase/cmd.rb +487 -0
  53. data/lib/usecase/config/README.md +57 -0
  54. data/lib/usecase/docker/README.md +146 -0
  55. data/lib/usecase/docker/docker.rb +49 -0
  56. data/lib/usecase/edit/README.md +43 -0
  57. data/lib/usecase/edit/delete.rb +46 -0
  58. data/lib/usecase/export.rb +40 -0
  59. data/lib/usecase/files/README.md +37 -0
  60. data/lib/usecase/files/eject.rb +56 -0
  61. data/lib/usecase/files/file_me.rb +78 -0
  62. data/lib/usecase/files/read.rb +169 -0
  63. data/lib/usecase/files/write.rb +89 -0
  64. data/lib/usecase/goto.rb +57 -0
  65. data/lib/usecase/id.rb +36 -0
  66. data/lib/usecase/import.rb +157 -0
  67. data/lib/usecase/init.rb +63 -0
  68. data/lib/usecase/jenkins/README.md +146 -0
  69. data/lib/usecase/jenkins/jenkins.rb +208 -0
  70. data/lib/usecase/login.rb +71 -0
  71. data/lib/usecase/logout.rb +28 -0
  72. data/lib/usecase/open.rb +71 -0
  73. data/lib/usecase/print.rb +40 -0
  74. data/lib/usecase/put.rb +81 -0
  75. data/lib/usecase/set.rb +44 -0
  76. data/lib/usecase/show.rb +138 -0
  77. data/lib/usecase/terraform/README.md +91 -0
  78. data/lib/usecase/terraform/terraform.rb +121 -0
  79. data/lib/usecase/token.rb +35 -0
  80. data/lib/usecase/update/README.md +55 -0
  81. data/lib/usecase/update/rename.rb +180 -0
  82. data/lib/usecase/use.rb +41 -0
  83. data/lib/usecase/verse.rb +20 -0
  84. data/lib/usecase/view.rb +71 -0
  85. data/lib/usecase/vpn/README.md +150 -0
  86. data/lib/usecase/vpn/vpn.ini +31 -0
  87. data/lib/usecase/vpn/vpn.rb +54 -0
  88. data/lib/version.rb +3 -0
  89. data/safedb.gemspec +34 -0
  90. metadata +193 -0
@@ -0,0 +1,487 @@
1
+ #!/usr/bin/ruby
2
+ # coding: utf-8
3
+
4
+ module SafeDb
5
+
6
+ # The parent SafeDb use case is designed to be extended by the cli
7
+ # (command line) use cases like {SafeDb::Open}, {SafeDb::Put} and
8
+ # {SafeDb::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 {SafeDb::UseCase} 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
+ #
17
+ # == Machine (Workstation) Configuration File
18
+ #
19
+ # The global configuration filepath is found off the home directory using {Dir.home}.
20
+ #
21
+ # ~/.safedb.net/safedb.net.configuration.ini
22
+ #
23
+ # The global configuration file in INI format is managed through the methods
24
+ #
25
+ # - {grab} read the value at key_name from the default section
26
+ # - {stash} put directive key/value pair in default section
27
+ # - {read} read the value at key_name from the parameter section
28
+ # - {write} put directive key/value pair in parameter section
29
+ class UseCase
30
+
31
+ # This variable should be set to true if the use case call
32
+ # originates from a shell different from the one through which
33
+ # the login ocurred.
34
+ #
35
+ # To proceed, the shell that hosted the safe login must be a
36
+ # parent or at least an ancestor of this shell.
37
+ #
38
+ # This variable need not be set if the login shell is the direct
39
+ # parent of this one (the every day manual usage scenario).
40
+ #
41
+ # If however the login occurred from a grand or great grandparent
42
+ # shell (as is the case when nested scripts make an agent-like call),
43
+ # this variable must be set to true.
44
+ attr_writer :from_script
45
+
46
+ # This prefix denotes keys and their values should be posted as environment
47
+ # variables within the context (for example terraform) before instigating the
48
+ # main action like terraform apply.
49
+ ENV_VAR_PREFIX_A = "evar."
50
+ ENV_VAR_PREFIX_B = "@evar."
51
+
52
+ # This prefix precedes keynames whose map value represents a file object.
53
+ FILE_KEY_PREFIX = "file::"
54
+
55
+ # The base64 encoded representation of the file content is placed into
56
+ # a map with this keyname.
57
+ FILE_CONTENT_KEY = "content64"
58
+
59
+ # The file base name is placed into a map with this keyname.
60
+ FILE_NAME_KEY = "filename"
61
+
62
+
63
+ # This is the root command typed into the shell to invoke one of the
64
+ # safe use cases.
65
+ COMMANDMENT = "safe"
66
+
67
+
68
+ # The name of the environment variable that will hold the session token
69
+ # generated by {self.generate_session_token}. This environment variable
70
+ # is typically instantiated either manually (for ad-hoc use) or through
71
+ # media such as dot profile.
72
+ ENV_VAR_KEY_NAME = "SAFE_TTY_TOKEN"
73
+
74
+
75
+ # If and when this command line credentials management app needs to write
76
+ # any configuration directives to the machine's userspace it will use this
77
+ # constant to denote the (off-home) directory name.
78
+ APP_DIR_NAME = "safedb.net"
79
+
80
+
81
+ # Get the master database. This behaviour can only complete
82
+ # correctly if a successful login precedes this call either
83
+ # in this or an ancestral shell environment.
84
+ #
85
+ # @return [Hash]
86
+ # the hash data structure returned represents the master
87
+ # database.
88
+ def get_master_database
89
+
90
+ begin
91
+
92
+ log.info(x) { "Request for master db with from_script set to #{@from_script}" }
93
+ return KeyApi.read_master_db( @from_script )
94
+
95
+ rescue OpenSSL::Cipher::CipherError => e
96
+
97
+ log.fatal(x) { "Exception getting master db for the safe book." }
98
+ log.fatal(x) { "The from_script parameter came set as [ #{@from_script} ]" }
99
+ log.fatal(x) { "The exception message is ~> [[ #{e.message} ]]" }
100
+ e.backtrace.log_lines
101
+ abort e.message
102
+
103
+ end
104
+
105
+ end
106
+
107
+ # The path to the initial configuration file below the user's home
108
+ # directory. The directory name the configuration file sits in is
109
+ # a dot prefixed context name derived from the value inside the
110
+ # {APP_DIR_NAME} constant.
111
+ #
112
+ # ~/.<<context-name>>/<<context-name>>-configuration.ini
113
+ #
114
+ # You can see the filename too is derived from the context with a
115
+ # trailing string ending in <b>.ini</b>.
116
+ #
117
+ # @return [String] full path to the context configuration file
118
+ def config_file
119
+ return File.join config_directory(), "#{APP_DIR_NAME}.configuration.ini"
120
+ end
121
+
122
+
123
+ # This method returns the absolute path to the directory that the
124
+ # configuration file sits in. It is basically just the dot prefixed
125
+ # context name (the {APP_DIR_NAME} constant).
126
+ #
127
+ # ~/.<<context-name>>
128
+ #
129
+ # @return [String] path to directory holding context configuration file
130
+ def config_directory
131
+ return File.join( Dir.home, ".#{APP_DIR_NAME}" )
132
+ end
133
+
134
+
135
+ # Execute the use cases's flow from beginning when
136
+ # you validate the input and parameters through the
137
+ # memorize, execute and the final cleanup.
138
+ def flow_of_events
139
+
140
+ check_pre_conditions
141
+ execute
142
+ cleanup
143
+ check_post_conditions
144
+
145
+ end
146
+
147
+
148
+ # Validate the input parameters and check that the current
149
+ # state is perfect for executing the use case.
150
+ #
151
+ # If either of the above fail - the validation function should
152
+ # set a human readable string and then throw an exception.
153
+ def check_pre_conditions
154
+
155
+ begin
156
+
157
+ pre_validation
158
+
159
+ rescue OpenError::CliError => e
160
+
161
+ puts ""
162
+ puts "Your command did not complete successfully."
163
+ puts "Pre validation checks failed."
164
+ puts ""
165
+ puts " => #{e.message}"
166
+ puts ""
167
+ abort e.message
168
+ end
169
+
170
+ end
171
+
172
+
173
+ # Override me if you need to
174
+ def pre_validation
175
+
176
+ end
177
+
178
+
179
+ # After the main flow of events certain state conditions
180
+ # must hold true thus demonstrating that the observable
181
+ # value has indeed ben delivered.
182
+ #
183
+ # Child classes should subclass this method and place any
184
+ # post execution (post condition) checks in it and then
185
+ # make a call to this method through the "super" keyword.
186
+ def check_post_conditions
187
+
188
+ begin
189
+
190
+ post_validation
191
+
192
+ rescue OpenError::CliError => e
193
+
194
+ puts ""
195
+ puts "Your command did not complete successfully."
196
+ puts "Post validation checks failed."
197
+ puts ""
198
+ puts " => #{e.message}"
199
+ #### puts " => #{e.culprit}"
200
+ puts ""
201
+ abort e.message
202
+ end
203
+
204
+ end
205
+
206
+
207
+ # Child classes should subclass this method and place any
208
+ # post execution (post condition) checks in it and then
209
+ # make a call to this method through the "super" keyword if
210
+ # this method gets any global behaviour in it worth calling.
211
+ def post_validation
212
+
213
+ end
214
+
215
+
216
+ # Execute the main flow of events of the use case. Any
217
+ # exceptions thrown are captured and if the instance
218
+ # variale [@human_readable_message] is set - tell the
219
+ # user about it. Without any message - just tell the
220
+ # user something went wrong and tell them where the logs
221
+ # are that might carry more information.
222
+ def execute
223
+
224
+ end
225
+
226
+
227
+ # If the use case validation went well, the memorization
228
+ # went well the
229
+ def cleanup
230
+
231
+ end
232
+
233
+
234
+ # This use case is initialized primary by resolving the configured
235
+ # +general and use case specific facts+. To access the general facts,
236
+ # a domain name is expected in the parameter delegated by the extension
237
+ # use case classes.
238
+ def initialize
239
+
240
+ class_name = self.class.name.split(":").last.downcase
241
+ is_no_token_usecase = [ "token", "init", "id" ].include? class_name
242
+ return if is_no_token_usecase
243
+
244
+ exit(100) unless ops_key_exists?
245
+
246
+ fact_filepath = File.sister_filepath( self, "ini", :execute )
247
+ log.info(x) { "Search location for INI factfile is [#{fact_filepath}]" }
248
+ return unless File.exists?( fact_filepath )
249
+
250
+ @facts = FactFind.new()
251
+ add_secret_facts @facts
252
+ @facts.assimilate_ini_file( fact_filepath )
253
+ @dictionary = @facts.f[ @facts.to_symbol( class_name ) ]
254
+
255
+ end
256
+
257
+
258
+ private
259
+
260
+
261
+ ENV_PATH = "env.path"
262
+ KEY_PATH = "key.path"
263
+ ENVELOPE_KEY_PREFIX = "envelope@"
264
+
265
+ LAST_ACCESSED = "last.accessed.time"
266
+
267
+ SESSION_DICT_LOCK_SIZE = 32
268
+
269
+ SESSION_DICT_LOCK_NAME = "crypted.session.dict.lock"
270
+
271
+ ENVELOPE_KEY_SIZE = 32
272
+
273
+ ENVELOPE_KEY_NAME = "crypted.envelope.key"
274
+
275
+ ENVELOPE_ID_SIZE = 16
276
+
277
+ ENVELOPE_ID_NAME = "crypted.envelope.id"
278
+
279
+ SESSION_ID_SIZE = 64
280
+
281
+ SESSION_FILENAME_ID_SIZE = 24
282
+
283
+ SESSION_START_TIMESTAMP_NAME = "session.creation.time"
284
+
285
+ MASTER_LOCK_KEY_NAME = "master.session.lock.key"
286
+
287
+ APPLICATION_GEM_NAME = "safedb.net"
288
+ APPLICATION_GEM_WEBSITE = "https://www.safedb.net"
289
+ APPLICATION_GITHUB_URL = "https://github.com/devops4me/safedb.net"
290
+
291
+
292
+ def add_secret_facts fact_db
293
+
294
+ master_db = get_master_database()
295
+ raise ArgumentError.new "There is no open chapter here." if unopened_envelope?( master_db )
296
+ chapter_id = ENVELOPE_KEY_PREFIX + master_db[ ENV_PATH ]
297
+ verse_id = master_db[ KEY_PATH ]
298
+ chapter_data = KeyDb.from_json( KeyApi.content_unlock( master_db[ chapter_id ] ) )
299
+ mini_dictionary = chapter_data[ master_db[ KEY_PATH ] ]
300
+
301
+ mini_dictionary.each do | key_str, value_str|
302
+ fact_db.assimilate_fact( "secrets", key_str, value_str )
303
+ end
304
+
305
+ end
306
+
307
+
308
+ def create_header()
309
+
310
+ return KeyApi.format_header(
311
+ SafeDb::VERSION,
312
+ APPLICATION_GEM_NAME,
313
+ APPLICATION_GITHUB_URL,
314
+ @domain_name
315
+ )
316
+
317
+ end
318
+
319
+
320
+
321
+ def ops_key_exists?
322
+
323
+ log_env()
324
+
325
+ if ( ENV.has_key? ENV_VAR_KEY_NAME )
326
+ return true
327
+ end
328
+
329
+ puts ""
330
+ puts "safe needs you to create a session key."
331
+ puts "To automate this step see the documentation."
332
+ puts "To create the key run the below command."
333
+ puts ""
334
+ puts " export #{ENV_VAR_KEY_NAME}=`#{COMMANDMENT} token`"
335
+ puts ""
336
+ puts "Those are backticks surrounding `#{COMMANDMENT} token`"
337
+ puts "Not apostrophes."
338
+ puts ""
339
+
340
+ return false
341
+
342
+ end
343
+
344
+
345
+ def log_env()
346
+
347
+ log.info(x) { "Gem Root Folder => #{Gem.dir()}" }
348
+ log.info(x) { "Gem Config File => #{Gem.config_file()}" }
349
+ log.info(x) { "Gem Binary Path => #{Gem.default_bindir()}" }
350
+ log.info(x) { "Gem Host Path => #{Gem.host()}" }
351
+ log.info(x) { "Gem Caller Folder => #{Gem.location_of_caller()}" }
352
+ log.info(x) { "Gem Paths List => #{Gem.path()}" }
353
+ log.info(x) { "Gem Platforms => #{Gem.platforms()}" }
354
+ log.info(x) { "Gem Ruby Version X => #{Gem.ruby()}" }
355
+ log.info(x) { "Gem Ruby Version Y => #{Gem::VERSION}" }
356
+ log.info(x) { "Gem Ruby Version Z => #{Gem.latest_rubygems_version()}" }
357
+ log.info(x) { "Gem User Folder => #{Gem.user_dir()}" }
358
+ log.info(x) { "Gem User Home => #{Gem.user_home()}" }
359
+
360
+ return
361
+
362
+ end
363
+
364
+
365
+ def unopened_envelope?( key_database )
366
+
367
+ return false if key_database.has_key?( ENV_PATH )
368
+ print_unopened_envelope()
369
+ return true
370
+
371
+ end
372
+
373
+
374
+ def print_unopened_envelope()
375
+
376
+ puts ""
377
+ puts "Problem - before creating, reading or changing data you"
378
+ puts "must first open a path to it like this."
379
+ puts ""
380
+ puts " #{COMMANDMENT} open email.accounts joe@gmail.com"
381
+ puts ""
382
+ puts " then you put data at that path"
383
+ puts ""
384
+ puts " #{COMMANDMENT} put username joebloggs"
385
+ puts " #{COMMANDMENT} put password jo3s-s3cr3t"
386
+ puts " #{COMMANDMENT} put phone-no 07123456789"
387
+ puts " #{COMMANDMENT} put question \"Mums maiden name\""
388
+ puts ""
389
+ puts " and why not read it back"
390
+ puts ""
391
+ puts " #{COMMANDMENT} get password"
392
+ puts ""
393
+ puts " then close the path."
394
+ puts ""
395
+ puts " #{COMMANDMENT} close"
396
+ puts ""
397
+
398
+ end
399
+
400
+
401
+ def print_already_logged_in
402
+
403
+ puts ""
404
+ puts "We are already logged in. Open a secret envelope, put, then seal."
405
+ puts ""
406
+ puts " #{COMMANDMENT} open aws.credentials:s3reader"
407
+ puts " #{COMMANDMENT} put access_key ABCD1234"
408
+ puts " #{COMMANDMENT} put secret_key FGHIJ56789"
409
+ puts " #{COMMANDMENT} put region_key eu-central-1"
410
+ puts " #{COMMANDMENT} seal"
411
+ puts ""
412
+
413
+ end
414
+
415
+
416
+ def print_already_initialized
417
+
418
+ puts ""
419
+ puts "You can go ahead and login."
420
+ puts "Your domain [#{@domain_name}] is already setup."
421
+ puts "You should already know the password."
422
+ puts ""
423
+ puts " #{COMMANDMENT} login #{@domain_name}"
424
+ puts ""
425
+
426
+ end
427
+
428
+
429
+ def print_domain_initialized
430
+
431
+ puts ""
432
+ puts "It is time to login."
433
+ puts "The protector keys for [#{@domain_name}] have been setup."
434
+ puts "From now on you simply login to use this domain."
435
+ puts ""
436
+ puts " #{COMMANDMENT} login #{@domain_name}"
437
+ puts ""
438
+
439
+ end
440
+
441
+
442
+ def print_not_initialized
443
+
444
+ puts ""
445
+ puts "Please initialize the app domain on this machine."
446
+ puts "Give a domain name and a folder for key storage."
447
+ puts ""
448
+ puts " #{COMMANDMENT} init <domain_name> \"$HOME/open.world\""
449
+ puts ""
450
+
451
+ end
452
+
453
+
454
+ def print_success_initializing
455
+
456
+ puts ""
457
+ puts "Success - now open a secret envelope, put, then seal."
458
+ puts ""
459
+ puts " #{COMMANDMENT} open aws.credentials:s3reader"
460
+ puts " #{COMMANDMENT} put access_key ABCD1234"
461
+ puts " #{COMMANDMENT} put secret_key FGHIJ56789"
462
+ puts " #{COMMANDMENT} put region_key eu-central-1"
463
+ puts " #{COMMANDMENT} seal"
464
+ puts ""
465
+
466
+ end
467
+
468
+
469
+ def print_login_success
470
+
471
+ puts ""
472
+ puts "Success - you are logged in."
473
+ puts ""
474
+ puts " #{COMMANDMENT} open aws.credentials:s3reader"
475
+ puts " #{COMMANDMENT} put access_key ABCD1234"
476
+ puts " #{COMMANDMENT} put secret_key FGHIJ56789"
477
+ puts " #{COMMANDMENT} put region_key eu-central-1"
478
+ puts " #{COMMANDMENT} seal"
479
+ puts ""
480
+
481
+ end
482
+
483
+
484
+ end
485
+
486
+
487
+ end
@@ -0,0 +1,57 @@
1
+
2
+ # Safe | Increasing Key Derivation Function Cost
3
+
4
+ Safe uses two **key derivation functions** (**BCrypt** and **PBKDF2**) to transform the human sourced password into a key. The only role the resulting key plays is the encryption and decryption of a large **highly random computer generated key** which in turn protects the **master database**.
5
+
6
+ ### Why are two (2) key derivation algorithms used?
7
+
8
+ Your credentials are still safe even in the rare case of a successful analytical attack being discovered on one of the algorithms.
9
+
10
+ ## Why High Computational Costs are Desirable?
11
+
12
+ Unlike most algorithms, key derivation functions **work best when they run slowly!** This protects against brute force attacks where attackers use "rainbow tables" or try to iterate over common passwords in an attempt to rederive the key.
13
+
14
+ Your responsibility is to make **safe as slow as is tolerable** by increasing the number of iterations required to derive each key.
15
+
16
+ ## Safe | Increasing the Cost of Both Key Derivation Functions
17
+
18
+ You should increase the cost of **safe's** key derivation functions until safe commands run as slow as is tolerably and no less!
19
+
20
+ ```bash
21
+ safe cost bcrypt 3
22
+ safe cost pbkdf2 4
23
+ ```
24
+
25
+ Both algorithms can be configured with a cost parameter from 1 to 7 inclusive. The default cost is 1 for both and is moderately secure and runs as slowly as is tolerable on an IBM ThinkPad laptop with an Intel Pentium i5 processor with 16G of RAM.
26
+
27
+ Note that PBKDF2 has no maximum. BCrypt limits the cost to 2^16.
28
+
29
+ <pre>
30
+ -------- - ------------ - --------------- - --------------------- - ---------------- -
31
+ | Cost | BCrypt | BCrypt | PBKDF2 | PBKDF2 |
32
+ | | Cost | Iterations | Cost | Iterations |
33
+ | ------ - ------------ - --------------- - --------------------- - ---------------- |
34
+ | 1 | 2^10 | 1,024 | 3^0 x 100,000 | 100,000 |
35
+ | 2 | 2^11 | 2,048 | 3^1 x 100,000 | 300,000 |
36
+ | 3 | 2^12 | 4,096 | 3^2 x 100,000 | 900,000 |
37
+ | 4 | 2^13 | 8,192 | 3^3 x 100,000 | 2,700,000 |
38
+ | 5 | 2^14 | 16,384 | 3^4 x 100,000 | 8,100,000 |
39
+ | 6 | 2^15 | 32,768 | 3^5 x 100,000 | 24,300,000 |
40
+ | 7 | 2^16 | 65,536 | 3^6 x 100,000 | 72,900,000 |
41
+ -------- - ------------ - --------------- - --------------------- - ---------------- -
42
+ </pre>
43
+
44
+ When you increase the cost **safe will become perceivably slower**. With a cost of 7, a laptop takes many minutes but an AWS cloud compute optimized M5 server crunches through in mere seconds.
45
+
46
+ ## What is Your Data Worth?
47
+
48
+ Attackers can bring a significant amount of modern data centre hardware to the table in order to access your credentials.
49
+
50
+ However, these computing resources cost money and the amount of money an attacker spends will be proportional to the perceived gains from a successfully attack. The bigger the dollar signs in their eyes, the more they will spend.
51
+
52
+ The default settings coupled with a **12 character password** takes (on average) 50 years to crack with computing resources that will cost $1,000 every single day.
53
+
54
+ ### Twenty Million Dollars
55
+
56
+ If what you are protecting is worth more than **$(50 x 366 x 1,000)**, you should use an at least 16 character password and increase the computational cost parameters for both key derivation functions.
57
+
@@ -0,0 +1,146 @@
1
+
2
+ # safe jenkins <command>
3
+
4
+
5
+ ### safe jenkins post [aws|docker|git] <<jenkins-host-url>> | introduction
6
+
7
+ Use **`safe jenkins post`** to inject both your **AWS IAM User** and **docker login/password** credentials into your Jenkins 2.0 continuous integration portal reachable by the **jenkins host url** given in the 4th parameter of the safe command.
8
+
9
+ ---
10
+
11
+ ## safe jenkins post | prerequisite
12
+
13
+ Before you can inject credentials into jenkins using **`safe jenkins post`** you must
14
+
15
+ - be logged into your safe
16
+ - have opened the appropriate chapter/verse
17
+ - have put the required credential key/value pairs into the safe
18
+ - have the jenkins service up and running
19
+
20
+ After the post (to jenkins), your continuous integration jobs will be able to access the credential values via their IDs as stated in the below table.
21
+
22
+ ---
23
+
24
+ ## safe jenkins post aws | key names table
25
+
26
+ As credentials are WORO (write once, read often), safe makes the reading part very very easy (and secure) so your effort is frontloaded.
27
+
28
+ | Safe Key | Jenkins Credential IDs | Environment Variable | Description |
29
+ |:-----------:|:----------------------:|:--------------------- |:-------------------------------------------------------- |
30
+ | @access.key | safe.aws.access.key | AWS_ACCESS_KEY_ID | The AWS IAM user's access key credential. |
31
+ | @secret.key | safe.aws.secret.key | AWS_SECRET_ACCESS_KEY | The AWS IAM user's secret key credential. |
32
+ | region.key | safe.aws.region.key | AWS_REGION | The AWS region key that your Jenkins service points to. |
33
+
34
+ So you can see that by convention, safe expects the credential keys in the safe to be named a particular way, and likewise, you can be assured of the IDs it gives those credentials when posted to Jenkins.
35
+
36
+
37
+ ## safe jenkins post | credentials lifecycle
38
+
39
+ The life of the credentials begins when you create an IAM user and record its access and secret keys. Then
40
+
41
+ - you login to safe and store the 3 keys and their values
42
+ - safe jenkins post will read the values and post them to Jenkins
43
+ - Jenkins stores the values in conjunction with the Jenkins Credential IDs
44
+ - pipeline jobs ask Jenkins to put the Credential ID values against environment variables
45
+ - tools like Terraform and AwsCli use the environment variables to work in the cloud
46
+
47
+
48
+ ## Jenkinsfile | Usage in Pipeline Jobs
49
+
50
+ Here is a pipeline declaration within a Jenkinsfile that asks Jenkins to put the credential values in its secrets store into the stated environment variables.
51
+
52
+ environment
53
+ {
54
+ AWS_ACCESS_KEY_ID = credentials( 'safe.aws.access.key' )
55
+ AWS_SECRET_ACCESS_KEY = credentials( 'safe.aws.secret.key' )
56
+ AWS_REGION = credentials( 'safe.aws.region.key' )
57
+ }
58
+
59
+ After **`safe jenkins post aws`** you can **click into the Credentials item in the Jenkins main menu** to assure yourself that the credentials have indeed been properly injected.
60
+
61
+ ---
62
+
63
+ ## How to Write AWS Credentials into your Safe
64
+
65
+ In order to **`safe terraform apply`** or **`safe jenkins post aws <<jenkins-host-url>>`** or `safe visit` you must first put those ubiquitous IAM programmatic user credentials into your safe.
66
+
67
+ $ safe login joebloggs.com # open the book
68
+
69
+ $ safe open iam dev.s3.reader # open chapter and verse
70
+ $ safe put @access.key ABCD1234EFGH5678 # Put IAM access key in safe
71
+ $ safe put @secret.key xyzabcd1234efgh5678 # Put IAM secret key in safe
72
+ $ safe put region.key eu-west-3 # infrastructure in Paris
73
+
74
+ $ safe open iam canary.admin # open chapter and verse
75
+ $ safe put @access.key 4321DCBA8765WXYZ # Put IAM access key in safe
76
+ $ safe put @secret.key 5678uvwx4321abcd9876 # Put IAM secret key in safe
77
+ $ safe put region.key eu-west-1 # infrastructure in Dublin
78
+
79
+ $ safe logout
80
+
81
+
82
+ ---
83
+
84
+
85
+ ## How to write DockerHub Credentials into your Safe
86
+
87
+ #### safe jenkins post docker https://jenkins.example.com
88
+
89
+ Before you can issue a **`safe jenkins post docker http://localhost:8080`** you must insert your docker login credentials in the form of a docker.username and @docker.password into your safe. Remember that any key starting with the `@ sign` tells the safe to keep it a secret like when you issue a **`safe show`** command.
90
+
91
+ $ safe login joebloggs.com # open the book
92
+ $ safe open docker production # at the docker (for production) chapter and verse
93
+ $ safe put docker.username admin # Put the Docker repository login docker.username into the safe
94
+ $ safe put @docker.password s3cr3t # Put the Docker repository login @docker.password into the safe
95
+ $ safe logout
96
+
97
+ When docker credentials are injected into a Jenkins service the safe will expect to find **lines** at the open chapter and verse location with key names **`docker.username`** and **`@docker.password`**.
98
+
99
+ The safe promises to inject credentials with an ID of **safe.docker.login.id** so any jenkins jobs that need to use the docker login docker.username and password must specify this ID when talking to the Jenkins credentials service.
100
+
101
+
102
+ ### DockerHub Credentials Inject Response
103
+
104
+ Here is an example of posting dockerhub credentials into a Jenkins service running on the local machine.
105
+
106
+ ``` bash
107
+ safe jenkins post docker http://localhost:8080
108
+ ```
109
+
110
+ If successful safe provides a polite response detailing what just happened.
111
+
112
+ ```
113
+ - Jenkins Host Url : http://localhost:8080/credentials/store/system/domain/_/createCredentials
114
+ - Credentials ID : safe.docker.login.id
115
+ - Inject Username : devops4me
116
+ - So what is this? : The docker repository login credentials in the shape of a username and password.
117
+
118
+ % Total % Received % Xferd Average Speed Time Time Time Current
119
+ Dload Upload Total Spent Left Speed
120
+ 100 428 0 0 100 428 0 47555 --:--:-- --:--:-- --:--:-- 47555
121
+ ```
122
+
123
+ ---
124
+
125
+
126
+ ## safe integrations | we need your help
127
+
128
+ **You can help to extend safe's integrations.**
129
+
130
+ By design - safe integrations are simple to write. They primarily integrate with producers and consumers. To deliver efficacy to devops engineers safe will endeavour to
131
+
132
+ - **send** credentials to **downstream consumers** and
133
+ - **receive** credentials from **upstream producers**
134
+
135
+ safe needs pull requests from the devops community and it promises to always strive to keep the task of writing an integration extremely simple.
136
+
137
+ ### integrations | what giving takes?
138
+
139
+ Currently, writing an integration entails delivering 3 or 4 artifacts which are
140
+
141
+ - 1 simple Ruby class
142
+ - 1 README.md documenting the command structure, the prerequisites and the expected outcome
143
+ - 1 class containing unit tests
144
+ - (optionaly) an INI file if many configuration and facts are involved
145
+
146
+ Giving doesn't take much so roll up your sleeves (or frocks) and get writing.