@@ -175,9 +175,12 @@ type MessageRequest struct {
175
175
// The object to act on.
176
176
Object Object ` json:"object"`
177
177
178
- // Configuration for handling TLS.
178
+ // Configuration for establishing TLS connections .
179
179
TLS TLSConfig ` json:"tls,omitempty"`
180
180
181
+ // A base64-encoded 32-byte encryption key for use with AES-GCM.
182
+ Encryption EncryptionConfig ` json:"encryption,omitempty"`
183
+
181
184
// Path to a file into which the message handler must write its MessageResponses.
182
185
ResponsePath string ` json:"response_path"`
183
186
}
@@ -189,6 +192,9 @@ type MessageResponse struct {
189
192
// The object.
190
193
Object Object ` json:"object"`
191
194
195
+ // Encrypted fields of the object.
196
+ Encrypted EncryptedObject ` json:"encrypted"`
197
+
192
198
// Metadata to associate with the object. Shown to the user.
193
199
Metadata []MetadataField ` json:"metadata,omitempty"`
194
200
}
@@ -203,6 +209,28 @@ type TLSConfig struct {
203
209
SkipVerification bool ` json:"skip_verification,omitempty"`
204
210
}
205
211
212
+ type EncryptionConfig struct {
213
+ // The encryption algorithm for the prototype to use.
214
+ //
215
+ // This value will be static, and changing it will imply a major bump to the
216
+ // Prototype protocol version. It is included here as a helpful indicator so
217
+ // that prototype authors don't have to guess at the payload.
218
+ Algorithm string ` json:"algorithm"`
219
+
220
+ // A base64-encoded 32-length key, unique to each message.
221
+ Key []byte ` json:"key"`
222
+ }
223
+
224
+ // EncryptedObject contains an AES-GCM encrypted JSON payload containing
225
+ // additional fields of the object.
226
+ type EncryptedObject struct {
227
+ // The base64-encoded encrypted payload.
228
+ Payload []byte ` json:"payload"`
229
+
230
+ // The base64-encrypted nonce.
231
+ Nonce []byte ` json:"nonce"`
232
+ }
233
+
206
234
// MetadataField represents a named bit of metadata associated to an object.
207
235
type MetadataField struct {
208
236
Name string ` json:"name"`
@@ -300,6 +328,111 @@ This response would be typical of a `check` that ran against a `git` repository
300
328
that had three commits.
301
329
302
330
331
+ ## Encryption
332
+
333
+ In order to use Prototypes for credential acquisition, there must be a way to
334
+ return object attributes which contain sensitive data without writing the data
335
+ to disk in plaintext.
336
+
337
+ A Prototype's ` MessageRequest ` may contain an ` EncryptionConfig ` which
338
+ specifies the encryption algorithm to use and any other necessary data for use
339
+ with the algorithm (such as a key).
340
+
341
+ The Prototypes protcol will only support one encryption algorithm at a time,
342
+ and if it needs to be changed, this will imply a ** major** bump to the protocol
343
+ version. This is to encourage phasing out support for no-longer-adequate
344
+ security algorithms.
345
+
346
+ The decision as to which algorithm to use for the initial version is currently
347
+ an open question, but the most likely candidate right now is AES-GCM, which is
348
+ the same algorithm used for database encryption in Concourse. Another candidate
349
+ may be NaCL. Note that whichever one we choose must be available in various
350
+ languages so that Prototype authors aren't restricted to any particular library
351
+ or language.
352
+
353
+ Assuming AES-GCM, the ` EncryptionConfig ` in the request will include a ` key `
354
+ field containing a base64-encoded 32-length key, and a ` nonce_size ` field
355
+ indicating the size of the nonce necessary to encrypt/decrypt:
356
+
357
+ ``` json
358
+ {
359
+ "object" : {
360
+ "uri" : " https://vault.example.com" ,
361
+ "client_token" : " 01234567889abcdef"
362
+ },
363
+ "encryption" : {
364
+ "algorithm" : " AES-GCM" ,
365
+ "key" : " aXzsY7eK/Jmn4L36eZSwAisyl6Q4LPFIVSGEE4XH0hA=" ,
366
+ "nonce_size" : 12
367
+ }
368
+ }
369
+ ```
370
+
371
+
372
+ It is the responsibility of the Prototype implementation to generate a nonce
373
+ value of the specified length. The Prototype would then marshal a JSON object
374
+ containing fields to be encrypted, encrypt it with the key and nonce, and
375
+ return the encrypted payload along with the nonce value in a ` EncryptedObject `
376
+ in the ` MessageResponse ` - both as base64-encoded values:
377
+
378
+ ``` json
379
+ {
380
+ "object" : {
381
+ "public" : " fields"
382
+ },
383
+ "encrypted" : {
384
+ "nonce" : " 6rYKFHXh43khqsVs" ,
385
+ "payload" : " St5pRZumCx75d2x2s3vIjsClUi9DqgnIoG2Slt2RoCvz"
386
+ }
387
+ }
388
+ ```
389
+
390
+ The encrypted payload above is ` {"some":"secret"} ` , so the above response
391
+ ultimately describes the following object:
392
+
393
+ ``` json
394
+ {
395
+ "public" : " fields" ,
396
+ "some" : " secret"
397
+ }
398
+ ```
399
+
400
+ ### Rationale for encryption technique
401
+
402
+ A few alternatives to this approach were considered:
403
+
404
+ * Initially, the use of HTTPS was appealing as it would avoid placing data on
405
+ disk entirely.
406
+
407
+ However this is a much more complicated architecture that raises many more
408
+ questions:
409
+
410
+ * What are the API endpoints?
411
+ * How are the requests routed?
412
+ * How is TLS configured?
413
+ * How is the response delivered?
414
+ * Regular HTTP response? If the ` web ` node detaches, how can we re-attach?
415
+ * Callbacks? What happens when the callback endpoint is down?
416
+ * Is it safe to retry requests?
417
+
418
+ * We could write the responses to ` tmpfs ` to ensure they only ever exist in
419
+ RAM.
420
+
421
+ The main downside is that operators would have to make sure that swap is
422
+ disabled so that data is never written to disk. This seems like a safe
423
+ assumption for Kubernetes but seems like an easy mistake to make for
424
+ operators managing their own VMs.
425
+
426
+ One advantage of the current approach is that it tells Concourse which fields
427
+ can be public and which fields contain sensitive information, while requiring
428
+ the sensitive information to be encrypted.
429
+
430
+ While this could be supported either way by specifying a field such as
431
+ ` expose":["public"] ` , it seems valuable to force Prototype authors to "do the
432
+ right thing" and encrypt the sensitive data, rather than allowing them to
433
+ simply hide it from the UI.
434
+
435
+
303
436
## Object Cloning
304
437
305
438
Technically, all parts of the prototocol have been specified, but there is a
0 commit comments