Skip to content

Commit cdcd7f1

Browse files
committed
updating per comments
Signed-off-by: lakshmimsft <ljavadekar@microsoft.com>
1 parent 76ffbf9 commit cdcd7f1

File tree

2 files changed

+66
-29
lines changed

2 files changed

+66
-29
lines changed

.github/config/en-custom.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,3 +1166,6 @@ terraformBackend
11661166
bicepAuthentication
11671167
filesystem
11681168
apiKey
1169+
decrypt
1170+
plaintext
1171+
decrypts

resources/2025-11-11-secrets-redactdata.md

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Redacting Sensitive Data for Radius Resource Types
22

3-
* **Author**: Lakshmi Javadekar (@lakshmimsft)
3+
* **Author**: `Lakshmi Javadekar (@lakshmimsft)`
44

55
## Overview
66

@@ -135,15 +135,15 @@ The design introduces encryption and redaction mechanisms that minimize sensitiv
135135
1. **Frontend (sync)**: Encrypts sensitive data (fields marked with `x-radius-sensitive` annotation), stores encrypted resource in database, queues async operation
136136
2. **Backend (async)**: Reads resource from database (with encrypted data), decrypts sensitive fields in memory, immediately nullifies sensitive fields in database
137137
3. **Recipe execution**: Executes recipe using in-memory decrypted data (never re-persisted to database)
138-
4. **Completion**: Updates resource with recipe outputs (outputResources). On success, returns normal success result. On failure, returns `Result{Requeue: false}` to prevent retries that would fail due to missing sensitive data.
138+
4. **Completion**: Updates resource with recipe outputs (`outputResources`). On success, returns normal success result. On failure, returns `Result{Requeue: false}` to prevent retries that would fail due to missing sensitive data.
139139

140140
Key components:
141141
- **Type Converter** (`bicep-tools/pkg/converter`): Maps fields with `x-radius-sensitive: true` annotation to `secureString`/`secureObject` Bicep types during YAML-to-Bicep type conversion.
142-
- **Frontend Controller**: Encrypts sensitive fields marked with `x-radius-sensitive` before database save using application-layer encryption (ChaCha20-Poly1305)
142+
- **Frontend Controller**: Encrypts sensitive fields marked with `x-radius-sensitive` before database save using application-layer encryption (`ChaCha20-Poly1305`)
143143
- **Backend Recipe Controller** (`pkg/portableresources/backend/controller/createorupdateresource.go`): Decrypts encrypted data, immediately redacts from database, then orchestrates recipe execution with in-memory data
144144
- **GET Controller**: Checks schema for `x-radius-sensitive` annotations and redacts fields when `provisioningState` is not in `Succeeded` state. For other states perform redaction to ensure no encrypted data exposure.
145145

146-
- **Encryption/Decryption Logic**: Application-layer encryption using Go `crypto/cipher` package with ChaCha20-Poly1305 AEAD cipher, root key stored in Kubernetes secret
146+
- **Encryption/Decryption Logic**: Application-layer encryption using Go `crypto/cipher` package with `ChaCha20-Poly1305 AEAD` cipher, root key stored in Kubernetes secret
147147
- **No-Retry Logic**: Backend controller returns `Result{Requeue: false}` on all failure paths for resources with sensitive fields, requiring user resubmission on failure. Successful operations return normal success result.
148148

149149
### Architecture Diagram
@@ -234,7 +234,7 @@ sequenceDiagram
234234

235235
##### Application-Layer Encryption Details
236236
- Store root key in a Kubernetes secret (similar to ucp-cert)
237-
- Use Go's `crypto/cipher` package with ChaCha20-Poly1305 AEAD cipher
237+
- Use Go's `crypto/cipher` package with `ChaCha20-Poly1305 AEAD` cipher
238238
- Generate unique nonce per encryption operation
239239
- Key rotation:
240240
- **Initial implementation (Manual)**:
@@ -249,7 +249,7 @@ sequenceDiagram
249249
1. System generates new key (version N+1)
250250
2. Adds to secret alongside existing keys
251251
3. Controllers hot-reload keys without restart
252-
4. New encryptions use latest key version
252+
4. New encryption uses latest key version
253253
5. Decryption uses versioned key (old data still readable during transition)
254254
6. After grace period (e.g., 24 hours), remove old key versions
255255
- Benefits: Zero-downtime rotation, gradual migration, no data loss
@@ -259,7 +259,7 @@ sequenceDiagram
259259
1. Check schema for fields with `x-radius-sensitive: true` annotation
260260
2. For each sensitive field:
261261
- Generate random 24-byte nonce
262-
- Encrypt field value using ChaCha20-Poly1305 with root key and nonce
262+
- Encrypt field value using `ChaCha20-Poly1305` with root key and nonce
263263
- Store as: `{"encrypted": base64(ciphertext), "nonce": base64(nonce)}`
264264
3. Save encrypted resource to database
265265

@@ -268,7 +268,7 @@ sequenceDiagram
268268
2. Check schema for fields with `x-radius-sensitive: true` annotation
269269
3. For each encrypted field:
270270
- Extract nonce and ciphertext
271-
- Decrypt using ChaCha20-Poly1305 with root key and nonce
271+
- Decrypt using `ChaCha20-Poly1305` with root key and nonce
272272
- Store decrypted value in memory only
273273
4. Nullify encrypted fields in resource: set to `null`
274274
5. Save sanitized resource to database
@@ -280,7 +280,7 @@ sequenceDiagram
280280
- Schema annotation missing: Skip encryption/decryption (treat as non-sensitive)
281281

282282
**Notes**:
283-
- OpenSSL is CLI-only; Go crypto APIs are used for programmatic encryption
283+
- `OpenSSL` is CLI-only; Go crypto APIs are used for programmatic encryption
284284
- **Alternative considered**: Using initContainer to load root key as a file was discussed but rejected due to:
285285
- Security concerns: Pod that can mount the volume could read the key
286286
- Managing writes on pod restarts and scaling complexity with multiple pods
@@ -299,7 +299,7 @@ sequenceDiagram
299299
- Kubernetes supports native etcd encryption via `EncryptionConfiguration`
300300
- Transparent to Radius application code
301301
- Document recommended encryption requirements.
302-
- **Setup**: Kubernetes admin configures encryption provider (AES-CBC, AES-GCM, or KMS)
302+
- **Setup**: Kubernetes admin configures encryption provider (`AES-CBC, AES-GCM, or KMS`)
303303
- **Reference**: https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
304304
- For future database backends, rely on native encryption at rest features of the database
305305

@@ -334,7 +334,7 @@ Document encryption requirement in Radius installation guides
334334
**Manual Re-deployment Overwrites Encrypted Data**:
335335
- Existing functionality includes the Frontend PUT handler checking for existing resource and it will overwrite encrypted data with new request (normal PUT behavior) when user re-runs the deployment.
336336

337-
**Future Enhancement**: Background cleanup job to identify `Failed` resources older than configurable threshold (e.g., 24 hours) and nullify any encrypted fields within those resource entries.
337+
**Cleanup**: Background cleanup job to identify `Failed` resources older than configurable threshold and nullify any encrypted fields within those resource entries.
338338

339339
#### Proposed Option
340340

@@ -470,7 +470,7 @@ No changes required to Core RP. The implementation is isolated to dynamic-rp.
470470

471471
The design employs a **defense-in-depth approach** with multiple security layers:
472472

473-
1. **Application-Layer Encryption (Primary)**: Sensitive fields marked with `x-radius-sensitive` are encrypted using ChaCha20-Poly1305 AEAD cipher before database storage
473+
1. **Application-Layer Encryption (Primary)**: Sensitive fields marked with `x-radius-sensitive` are encrypted using `ChaCha20-Poly1305 AEAD` cipher before database storage
474474
2. **Short Exposure Window**: Encrypted data exists in database for few seconds (frontend save → backend redaction)
475475
3. **In-Memory Processing**: Decrypted data exists only in process memory during recipe execution, never re-persisted
476476
4. **GET Redaction**: Sensitive fields redacted in API responses during deployment to prevent exposure
@@ -524,6 +524,13 @@ Organizations deploying Radius with resources containing `x-radius-sensitive` fi
524524
- Create Kubernetes Secret `radius-encryption-key` with 256-bit key
525525
- Restrict access via RBAC to Radius service accounts only
526526
- Rotate key periodically (manual process initially)
527+
528+
2. **Enable Kubernetes EncryptionConfiguration** (Required):
529+
- Configure etcd encryption to protect all Kubernetes Secrets at rest
530+
- This encrypts the root encryption key itself
531+
- Reference: https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
532+
- Note: Azure AKS and AWS EKS enable this by default
533+
527534

528535
2. **Network encryption** (Required):
529536
- TLS for all database connections
@@ -547,25 +554,25 @@ Organizations deploying Radius with resources containing `x-radius-sensitive` fi
547554
## Logging
548555

549556
1. **Encryption Success (Info level)**:
550-
"Encrypted sensitive field for storage" Fields: resourceID, resourceType, fieldName, operation="encrypt"
557+
`"Encrypted sensitive field for storage" Fields: resourceID, resourceType, fieldName, operation="encrypt"`
551558

552559
2. **Encryption Failure (Error level)**:
553-
"Failed to encrypt sensitive field" Fields: resourceID, resourceType, fieldName, error, operation="encrypt_failed"
560+
`"Failed to encrypt sensitive field" Fields: resourceID, resourceType, fieldName, error, operation="encrypt_failed"`
554561

555562
3. **Decryption Success (Info level)**:
556-
"Decrypted sensitive field from storage" Fields: resourceID, resourceType, fieldName, operation="decrypt"
563+
`"Decrypted sensitive field from storage" Fields: resourceID, resourceType, fieldName, operation="decrypt"`
557564

558565
4. **Decryption Failure (Error level)**:
559-
"Failed to decrypt sensitive field" Fields: resourceID, resourceType, fieldName, error, operation="decrypt_failed"
566+
`"Failed to decrypt sensitive field" Fields: resourceID, resourceType, fieldName, error, operation="decrypt_failed"`
560567

561568
5. **Redaction Success (Info level)**:
562-
"Redacted sensitive field from database" Fields: resourceID, resourceType, fieldName, operation="redact"
569+
`"Redacted sensitive field from database" Fields: resourceID, resourceType, fieldName, operation="redact"`
563570

564571
6. **Redaction Failure (Error level)**:
565-
"Failed to redact sensitive field from database" Fields: resourceID, resourceType, fieldName, error, operation="redact_failed"
572+
`"Failed to redact sensitive field from database" Fields: resourceID, resourceType, fieldName, error, operation="redact_failed"`
566573

567574
7. **Key Not Found (Error level)**:
568-
"Encryption key not found in Kubernetes Secret" Fields: secretName="radius-encryption-key", namespace, error
575+
`"Encryption key not found in Kubernetes Secret" Fields: secretName="radius-encryption-key", namespace, error`
569576

570577
### Troubleshooting
571578

@@ -613,20 +620,26 @@ Organizations deploying Radius with resources containing `x-radius-sensitive` fi
613620

614621
**2. Encryption/Decryption Infrastructure** (3 days):
615622
- Create `pkg/crypto/encryption` package
616-
- Implement ChaCha20-Poly1305 encryption/decryption
623+
- Implement `ChaCha20-Poly1305` encryption/decryption
617624
- Key management (load from Kubernetes Secret)
618625
- Nonce generation
619626
- Error handling
620627
- Unit tests for crypto operations
621628
- Integration with `pkg/components/secret` for key retrieval
622629

623-
**3. Schema Annotation Detection** (2 days):
630+
**3. Schema Annotation Detection** (3 days):
624631
- Implement schema fetching from UCP
625632
- Parse `x-radius-sensitive: true` annotations
626633
- Handle nested fields
627634
- Unit tests for annotation detection
628635

629-
**4. Kubernetes Secret Setup and Installation** (2-3 days):
636+
**4. Schema Validation** (1 days):
637+
- Update schema validation. When a resource type schema is registered (via `rad resource-type create`), validate:
638+
- Fields marked with `x-radius-sensitive: true` must have `type: string` or `type: object`
639+
- Return error if `x-radius-sensitive` is applied to other types (number, boolean, array, etc.)
640+
- Unit tests for schema validation logic
641+
642+
**5. Kubernetes Secret Setup and Installation** (2-3 days):
630643
- **Helm Chart Updates**:
631644
- Add template for `radius-encryption-key` Kubernetes Secret
632645
- Generate 256-bit random key during Helm install
@@ -643,30 +656,51 @@ Organizations deploying Radius with resources containing `x-radius-sensitive` fi
643656

644657
### Phase 2: Frontend Implementation (Sprint 2-3)
645658

646-
**4. Frontend Encryption** (3 days):
659+
**6. Frontend Encryption** (3 days):
647660
- Add encryption logic to frontend PUT handler
648661
- Encrypt fields marked with `x-radius-sensitive`
649662
- Store encrypted format: `{"encrypted": "...", "nonce": "..."}`
650663
- Error handling for encryption failures
651664
- Unit tests for frontend encryption
652665

653-
**5. GET Controller Redaction** (2 days):
666+
**7. GET Controller Redaction** (2 days):
654667
- Add redaction logic to GET controller
655668
- Check `provisioningState` and redact if not `Succeeded`
656669
- Handle schema fetch failures gracefully
657670
- Unit tests for GET redaction
658671

659672
### Phase 3: Backend Implementation (Sprint 2-3)
660673

661-
**6. Backend Decryption and Redaction** (5 days):
674+
**8. Backend Decryption and Redaction** (5 days):
662675
- Update `pkg/portableresources/backend/controller/createorupdateresource.go`
663676
- Add decryption after database read
664677
- Immediate redaction (save `data: null`) before recipe
665678
- Keep decrypted data in memory for recipe execution
666679
- Return `Result{Requeue: false}` on failures
667680
- Unit tests for backend flow
668681

669-
**7. Error Handling and Recovery** (2 days):
682+
**9. Background Cleanup Job for Lingering Encrypted Data** (5 days):
683+
- Create background controller/job to periodically scan for orphaned encrypted data
684+
- Identify resources with `provisioningState!= Succeeded` where `SystemData.LastModifiedAt` is older than configurable threshold
685+
- For each Failed resource:
686+
- Fetch resource schema
687+
- Check for fields with `x-radius-sensitive: true` annotation
688+
- Nullify any encrypted fields still present in the resource
689+
- Save cleaned resource back to database
690+
- Configuration:
691+
- Configurable cleanup interval
692+
- Configurable age threshold
693+
- Enable/disable cleanup via feature flag or configuration
694+
- Logging:
695+
- Log each cleanup operation (resourceID, fields nullified, timestamp)
696+
- Error handling:
697+
- Handle schema fetch failures gracefully
698+
- Continue processing other resources if one fails
699+
- Unit tests for cleanup logic
700+
- Integration tests verifying cleanup runs and nullifies encrypted data
701+
702+
703+
**10. Error Handling and Recovery** (4 days):
670704
- Implement comprehensive error handling
671705
- Add logging for all operations
672706
- Test failure scenarios
@@ -675,15 +709,15 @@ Organizations deploying Radius with resources containing `x-radius-sensitive` fi
675709

676710
### Phase 4: Testing + Documentation(Sprint 3-4)
677711

678-
**8. Integration Testing** (5 days):
712+
**11. Integration Testing** (5 days):
679713
- End-to-end encryption → decryption → redaction flow
680714
- Recipe execution with in-memory data
681715
- GET during deployment
682716
- Failure and recovery scenarios
683717
- Multi-backend recipe testing (K8s, Azure KV)
684718
- Performance testing
685719

686-
**9. Documentation** (2 days):
720+
**12. Documentation** (2 days):
687721
- User documentation for `x-radius-sensitive` annotation
688722
- Installation guide for encryption key setup
689723
- Troubleshooting guide
@@ -745,7 +779,7 @@ Pass sensitive data directly from frontend to async queue.without saving to data
745779

746780
## Design Review Notes
747781

748-
An earlier version of this design document focussed on detecting Radius.Security/secrets type and performing encryption/redaction for it's specific field. After discussions we updated the approach to now detect `x-radius-sensitive` annotation in any type and perform encryption/redaction for the sensitive fields.
782+
An earlier version of this design document focused on detecting Radius.Security/secrets type and performing encryption/redaction for it's specific field. After discussions we updated the approach to now detect `x-radius-sensitive` annotation in any type and perform encryption/redaction for the sensitive fields.
749783
---
750784

751785
## References

0 commit comments

Comments
 (0)