Skip to content

Move UIN hash pepper to SSM instead of secret#449

Merged
devksingh4 merged 3 commits intomainfrom
dsingh14/move-to-ssm
Jan 4, 2026
Merged

Move UIN hash pepper to SSM instead of secret#449
devksingh4 merged 3 commits intomainfrom
dsingh14/move-to-ssm

Conversation

@devksingh4
Copy link
Member

@devksingh4 devksingh4 commented Jan 4, 2026

Summary by CodeRabbit

  • New Features

    • App now fetches configuration from AWS SSM Parameter Store in addition to Secrets Manager.
  • Tools

    • Added CLI utility to create/update secure SSM parameters across multiple regions.
  • Documentation

    • New guide for creating and managing SSM parameters across regions.
  • Chores

    • Updated AWS SDK versions and runtime permissions to allow SSM access; configuration surface updated to use parameter IDs.
  • Tests

    • Test setup updated to mock SSM Parameter Store retrievals.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 4, 2026

Caution

Review failed

The pull request is closed.

Walkthrough

Adds SSM Parameter Store as a configuration source: new getSsmParameter util, refreshSecretConfig now fetches and merges Secrets Manager and SSM values into app.secretConfig, config types updated with ConfigurationParameterIds, IAM/SSM/KMS permissions added, and tooling for multi-region SSM parameter creation introduced.

Changes

Cohort / File(s) Summary
API entry & runtime
src/api/index.ts
refreshSecretConfig now logs and fetches ConfigurationParameterIds, invokes SSM retrieval for each parameter, maps parameter paths to compact keys, and merges SSM results with Secrets Manager secrets into app.secretConfig.
Utilities (SSM helper)
src/api/utils.ts
Added getSsmParameter (exports GetSsmParameterInputs and function) using SSMClient + GetParameterCommand, with decryption, error handling, and logging.
Dependencies
src/api/package.json
Bumped @aws-sdk/s3-request-presigner & @aws-sdk/client-s3 to ^3.922.0; added @aws-sdk/client-ssm ^3.922.0.
Configuration types & defaults
src/common/config.ts
Added ConfigurationParameterIds: string[] to config shape; removed UinHashingSecret from GenericConfigType and genericConfig; updated environmentConfig.dev and prod entries.
IAM / Terraform
terraform/modules/lambdas/main.tf
Added IAM policy statements: kms:Decrypt on alias/aws/ssm and SSM read actions (ssm:GetParameter, ssm:GetParameters, ssm:GetParametersByPath) for infra-core-api/*.
Tests & test data
tests/unit/secret.testdata.ts, tests/unit/vitest.setup.ts
Removed uinSecretJson test data; test setup now mocks SSMClient GetParameterCommand to return the UIN pepper and adjusted imports/fixtures accordingly.
Utilities docs & tooling
utils/README.md, utils/setSsmParameter.py
Added README about multi-region SSM parameter creation and a Python script to put SecureString parameters across regions concurrently with per-region reporting and error handling.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application Startup
    participant Refresh as refreshSecretConfig()
    participant SM as AWS Secrets Manager
    participant SSM as AWS SSM Parameter Store
    participant Config as app.secretConfig

    App->>Refresh: Initialize configuration
    rect rgb(240,248,255)
        Note over Refresh: Fetch secrets (existing flow)
        Refresh->>SM: Get secrets (ConfigurationSecretIds)
        SM-->>Refresh: allSecrets[]
    end

    rect rgb(250,245,230)
        Note over Refresh: Fetch SSM parameters (new)
        Refresh->>SSM: For each ConfigurationParameterIds -> getSsmParameter()
        SSM-->>Refresh: allParameters[] (compact-key/value)
    end

    rect rgb(240,255,240)
        Note over Refresh: Merge and set config
        Refresh->>Refresh: Combine allSecrets + allParameters -> allConfig
        Refresh->>Config: Reduce allConfig -> app.secretConfig
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately describes the main change: migrating UIN hash pepper configuration from AWS Secrets Manager to AWS SSM Parameter Store.

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 3d27540 and 80d452d.

📒 Files selected for processing (1)
  • src/common/config.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 4, 2026

💰 Infracost report

Monthly estimate generated

This comment will be updated when code changes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (6)
utils/setSsmParameter.py (3)

59-59: Use explicit Optional type annotation.

The function signature uses implicit Optional which is prohibited by PEP 484. Update to use explicit union syntax.

🔎 Proposed fix
-def put_parameter_multi_region(name: str, value: str, regions: list[str] = None) -> list[dict]:
+def put_parameter_multi_region(name: str, value: str, regions: list[str] | None = None) -> list[dict]:

64-68: Concurrent execution could exceed AWS rate limits.

The ThreadPoolExecutor with max_workers=len(target_regions) creates one thread per region. With the default 2 regions this is fine, but if users override with many regions, this could trigger AWS API rate limits for PutParameter operations.

Consider adding a configurable max_workers limit (e.g., 5) to prevent rate limit issues when working with many regions.

🔎 Proposed fix
-    with ThreadPoolExecutor(max_workers=len(target_regions)) as executor:
+    # Limit concurrent workers to avoid AWS rate limits
+    max_workers = min(5, len(target_regions))
+    with ThreadPoolExecutor(max_workers=max_workers) as executor:

89-90: Consider adding confirmation for sensitive operations.

The script uses getpass to securely read the parameter value (good!), but it immediately proceeds with multi-region updates without confirmation. For sensitive parameters like UIN hash pepper, consider adding a confirmation prompt showing which regions will be updated.

🔎 Example implementation
     value = getpass.getpass("Enter parameter value: ")
 
     if not value:
         print("Error: Parameter value cannot be empty")
         sys.exit(1)
 
     target_regions = args.regions or REGIONS
+    
+    # Confirmation prompt for safety
+    print(f"\nThis will update '{args.name}' in: {', '.join(target_regions)}")
+    confirm = input("Continue? (yes/no): ").lower()
+    if confirm != "yes":
+        print("Aborted")
+        sys.exit(0)
+    
     print(f"\nSetting parameter '{args.name}' in {len(target_regions)} region(s)...\n")
utils/README.md (1)

1-7: Add usage examples and prerequisites.

The documentation is minimal. Consider adding:

  • Prerequisites (AWS credentials, Python dependencies)
  • Usage example with actual command syntax
  • Link to the Python script
🔎 Enhanced documentation example
 # Utils
 
 Random useful scripts
 
 ## Create Multiregion Parameter
 
-Create the same AWS SSM ParameterStore Secure string in all regions we need.
+Create the same AWS SSM ParameterStore SecureString parameter across multiple regions.
+
+### Prerequisites
+- Python 3.9+ with boto3
+- AWS credentials configured (AWS CLI or environment variables)
+- Appropriate IAM permissions for SSM PutParameter and KMS Decrypt
+
+### Usage
+
+```bash
+# Create parameter in default regions (us-east-2, us-west-2)
+python setSsmParameter.py /infra-core-api/uin-pepper
+
+# Override regions
+python setSsmParameter.py /infra-core-api/uin-pepper --regions us-east-1 us-west-1
+```
+
+The script will prompt for the parameter value securely (not displayed or logged).
src/api/utils.ts (2)

46-50: Remove redundant | undefined from optional parameter.

The ? optional marker already makes the type SSMClient | undefined, so the explicit | undefined is redundant.

🔎 Proposed fix
 type GetSsmParameterInputs = {
   parameterName: string;
   logger: ValidLoggers;
-  ssmClient?: SSMClient | undefined;
+  ssmClient?: SSMClient;
 };

57-62: Consider validating parameter name format.

AWS SSM parameter names must follow specific rules (start with /, valid characters, max length). Consider adding validation to fail fast with a clear error message rather than making an API call that will fail.

🔎 Example validation
 export const getSsmParameter = async ({
   parameterName,
   logger,
   ssmClient,
 }: GetSsmParameterInputs) => {
+  // Validate parameter name format
+  if (!parameterName.startsWith('/')) {
+    logger.error(`Invalid parameter name: ${parameterName} (must start with /)`);
+    throw new InternalServerError({ message: "Invalid parameter name format" });
+  }
+  
   const client = ssmClient || new SSMClient({});
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 6b5c7e0 and edd1482.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (7)
  • src/api/index.ts
  • src/api/package.json
  • src/api/utils.ts
  • src/common/config.ts
  • terraform/modules/lambdas/main.tf
  • utils/README.md
  • utils/setSsmParameter.py
🧰 Additional context used
🧬 Code graph analysis (2)
src/api/index.ts (1)
src/api/utils.ts (1)
  • getSsmParameter (52-77)
src/api/utils.ts (3)
src/api/types.d.ts (1)
  • ValidLoggers (14-14)
src/api/sqs/logger.ts (1)
  • logger (2-2)
src/common/errors/index.ts (1)
  • InternalServerError (74-86)
🪛 Ruff (0.14.10)
utils/setSsmParameter.py

51-51: Do not catch blind exception: Exception

(BLE001)


59-59: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build Application
  • GitHub Check: Run Unit Tests
🔇 Additional comments (7)
terraform/modules/lambdas/main.tf (2)

212-215: Secrets Manager reference remains for uin-pepper.

The IAM policy still grants access to infra-core-api-uin-pepper in Secrets Manager (line 214), but the PR title indicates moving the UIN hash pepper to SSM. This suggests either:

  1. A gradual migration is planned (SSM added, Secrets Manager to be removed later)
  2. Both locations will be supported for backward compatibility

Please clarify the migration strategy and consider adding a TODO comment or tracking issue if the Secrets Manager permission is intended to be removed in a future PR.


193-208: Remove unused SSM action permissions to follow least-privilege principle.

The KMS resource ARN for the AWS-managed SSM key is correctly formatted. However, the SSM policy statement grants three actions (ssm:GetParameter, ssm:GetParameters, ssm:GetParametersByPath) when only ssm:GetParameter (singular) is actually used in the codebase. Remove the unused ssm:GetParameters and ssm:GetParametersByPath actions:

Action = [
  "ssm:GetParameter"
],
utils/setSsmParameter.py (1)

21-56: LGTM - Robust error handling for regional parameter updates.

The function correctly handles AWS-specific exceptions and returns structured results. The broad exception catch on line 51 is acceptable here since it's in an error-recording context where all exceptions need to be captured and returned to the caller.

src/api/package.json (1)

18-19: LGTM - Consistent AWS SDK versions.

The AWS SDK dependencies are updated to version 3.922.0 consistently, and the new @aws-sdk/client-ssm dependency supports the SSM parameter retrieval functionality.

Also applies to: 26-26

src/api/utils.ts (1)

7-7: No action needed: BASE_RETRY_DELAY is actively used in the exponential backoff calculation.

The BASE_RETRY_DELAY constant at line 7 is used at line 32 in the retryDynamoTransactionWithBackoff function: const exponentialDelay = BASE_RETRY_DELAY * 2 ** attempt;. Since this is a new file where both the constant and the function are introduced together, they form an intended, cohesive implementation for retry logic with exponential backoff.

Likely an incorrect or invalid review comment.

src/api/index.ts (1)

65-66: LGTM: Imports are correct.

The new imports for SSM functionality are properly added and will be used in the refreshSecretConfig function.

src/common/config.ts (1)

29-29: LGTM: New configuration field added correctly.

The ConfigurationParameterIds field is properly typed and integrated into the ConfigType interface.

@devksingh4 devksingh4 merged commit 9993761 into main Jan 4, 2026
7 of 9 checks passed
@devksingh4 devksingh4 deleted the dsingh14/move-to-ssm branch January 4, 2026 02:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant