diff --git a/airflow_secrets.tf b/airflow_secrets.tf index 85218fe..9eeb0ca 100644 --- a/airflow_secrets.tf +++ b/airflow_secrets.tf @@ -9,6 +9,8 @@ resource "random_password" "airflow_auth" { min_numeric = 1 min_special = 1 special = true + # YAML-safe special characters only - excludes quotes, backslashes, and other problematic chars + override_special = "!@#$%^&*()-_=+[]{}:?" } resource "kubernetes_secret_v1" "airflow_auth" { diff --git a/modules/opensearch/main.tf b/modules/opensearch/main.tf index 53ddd7a..7897ef3 100644 --- a/modules/opensearch/main.tf +++ b/modules/opensearch/main.tf @@ -92,7 +92,8 @@ resource "random_password" "opensearch_password" { min_numeric = 1 min_special = 1 special = true - override_special = "_-." + # YAML-safe special characters only - excludes quotes, backslashes, and other problematic chars + override_special = "!@#$%^&*()-_=+[]{}:?" } resource "kubernetes_secret_v1" "opensearch_credentials" { diff --git a/modules/rds/main.tf b/modules/rds/main.tf index d7856da..aaf3ca8 100644 --- a/modules/rds/main.tf +++ b/modules/rds/main.tf @@ -7,7 +7,8 @@ resource "random_password" "db_password" { min_numeric = 1 min_special = 1 special = true - override_special = "#^&*()-_=+[]{}:?" + # YAML-safe special characters only - excludes quotes, backslashes, and other problematic chars + override_special = "!@#$%^&*()-_=+[]{}:?" } module "rds" { diff --git a/test_password_security.tf b/test_password_security.tf new file mode 100644 index 0000000..b5402d3 --- /dev/null +++ b/test_password_security.tf @@ -0,0 +1,97 @@ +# Password Security Testing for Issue #14 +# This file provides validation and testing for YAML-safe password generation + +# Test random password generation with YAML-safe characters +resource "random_password" "test_password_yaml_safe" { + length = 16 + min_upper = 1 + min_lower = 1 + min_numeric = 1 + min_special = 1 + special = true + # Only YAML-safe special characters + override_special = "!@#$%^&*()-_=+[]{}:?" +} + +# Validation: Ensure password can be safely encoded in YAML +locals { + test_password_validation = { + raw_password = random_password.test_password_yaml_safe.result + yaml_encoded = yamlencode(random_password.test_password_yaml_safe.result) + is_yaml_safe = can(yamlencode(random_password.test_password_yaml_safe.result)) + contains_quotes = contains(split("", random_password.test_password_yaml_safe.result), "\"") + contains_backslash = contains(split("", random_password.test_password_yaml_safe.result), "\\") + } +} + +# Output validation results for testing +output "password_security_test" { + sensitive = true + value = { + yaml_safe_check = local.test_password_validation.is_yaml_safe + contains_quotes = local.test_password_validation.contains_quotes + contains_backslash = local.test_password_validation.contains_backslash + # Don't output the actual password for security + password_length = length(local.test_password_validation.raw_password) + } + description = "Password security validation results - all should be: yaml_safe=true, contains_quotes=false, contains_backslash=false" +} + +# Test YAML template rendering with special characters +resource "local_file" "test_yaml_template" { + content = templatefile("${path.module}/test_yaml_template.tftpl", { + test_password = random_password.test_password_yaml_safe.result + }) + filename = "/tmp/test_yaml_output.yaml" +} + +# Validate that the generated YAML is parseable (simplified validation without PyYAML dependency) +data "external" "yaml_validation" { + program = ["python3", "-c", <<-EOT +import json +import sys +import os + +try: + # Check if file exists and is readable + if not os.path.exists('/tmp/test_yaml_output.yaml'): + print(json.dumps({"valid": "false", "error": "YAML file not found"})) + sys.exit(0) + + with open('/tmp/test_yaml_output.yaml', 'r') as f: + content = f.read() + + # Basic YAML syntax validation - check for common YAML issues + lines = content.strip().split('\n') + errors = [] + + for i, line in enumerate(lines, 1): + line = line.strip() + if not line or line.startswith('#'): + continue + + # Check for unclosed quotes + if line.count('"') % 2 != 0: + errors.append(f"Unclosed quotes on line {i}") + + # Check for unescaped special characters outside quotes + if '\\' in line and '"' not in line: + errors.append(f"Unescaped backslash on line {i}") + + if errors: + print(json.dumps({"valid": "false", "error": "; ".join(errors)})) + else: + print(json.dumps({"valid": "true", "error": "none", "note": "Basic syntax validation passed"})) + +except Exception as e: + print(json.dumps({"valid": "false", "error": str(e)})) +EOT + ] + + depends_on = [local_file.test_yaml_template] +} + +output "yaml_parsing_test" { + value = data.external.yaml_validation.result + description = "YAML parsing validation - should show valid=true" +} \ No newline at end of file diff --git a/test_yaml_template.tftpl b/test_yaml_template.tftpl new file mode 100644 index 0000000..01377fe --- /dev/null +++ b/test_yaml_template.tftpl @@ -0,0 +1,12 @@ +# Test YAML template to validate password handling +test_config: + password: "${test_password}" + # Test that password is properly rendered without breaking YAML syntax + database: + auth: + password: "${test_password}" + services: + - name: "test-service" + password: "${test_password}" + config: + secret: "${test_password}" \ No newline at end of file