Main execution function for the x25519_pubkey Ansible module. Handles parameter validation, private key processing, public key derivation, and optional file output with idempotent behavior.
()
| 30 | |
| 31 | |
| 32 | def run_module(): |
| 33 | """ |
| 34 | Main execution function for the x25519_pubkey Ansible module. |
| 35 | |
| 36 | Handles parameter validation, private key processing, public key derivation, |
| 37 | and optional file output with idempotent behavior. |
| 38 | """ |
| 39 | module_args = { |
| 40 | "private_key_b64": {"type": "str", "required": False}, |
| 41 | "private_key_path": {"type": "path", "required": False}, |
| 42 | "public_key_path": {"type": "path", "required": False}, |
| 43 | } |
| 44 | |
| 45 | result = { |
| 46 | "changed": False, |
| 47 | "public_key": "", |
| 48 | } |
| 49 | |
| 50 | module = AnsibleModule( |
| 51 | argument_spec=module_args, required_one_of=[["private_key_b64", "private_key_path"]], supports_check_mode=True |
| 52 | ) |
| 53 | |
| 54 | priv_b64 = None |
| 55 | |
| 56 | if module.params["private_key_path"]: |
| 57 | try: |
| 58 | with open(module.params["private_key_path"], "rb") as f: |
| 59 | data = f.read() |
| 60 | try: |
| 61 | # First attempt: assume file contains base64 text data |
| 62 | # Strip whitespace from edges for text files (safe for base64 strings) |
| 63 | stripped_data = data.strip() |
| 64 | base64.b64decode(stripped_data, validate=True) |
| 65 | priv_b64 = stripped_data.decode() |
| 66 | except (base64.binascii.Error, ValueError): |
| 67 | # Second attempt: assume file contains raw binary data |
| 68 | # CRITICAL: Do NOT strip raw binary data - X25519 keys can contain |
| 69 | # whitespace-like bytes (0x09, 0x0A, etc.) that must be preserved |
| 70 | # Stripping would corrupt the key and cause "got 31 bytes" errors |
| 71 | if len(data) != 32: |
| 72 | module.fail_json( |
| 73 | msg=f"Private key file must be either base64 or exactly 32 raw bytes, got {len(data)} bytes" |
| 74 | ) |
| 75 | priv_b64 = base64.b64encode(data).decode() |
| 76 | except OSError as e: |
| 77 | module.fail_json(msg=f"Failed to read private key file: {e}") |
| 78 | else: |
| 79 | priv_b64 = module.params["private_key_b64"] |
| 80 | |
| 81 | # Validate input parameters |
| 82 | if not priv_b64: |
| 83 | module.fail_json(msg="No private key provided") |
| 84 | |
| 85 | try: |
| 86 | priv_raw = base64.b64decode(priv_b64, validate=True) |
| 87 | except Exception as e: |
| 88 | module.fail_json(msg=f"Invalid base64 private key format: {e}") |
| 89 |