# Ansible Vault SSH Key Management Guide ## Overview This document explains how to securely manage SSH private keys using Ansible Vault and the copy module with decryption capabilities, including proper playbook configuration to handle decryption before fact gathering. ## Important: Playbook Configuration ### Critical Setup Requirements ```yaml --- - name: Deploy with encrypted SSH key hosts: all gather_facts: false # ⚠️ ESSENTIAL - prevents early SSH connection attempts vars: ssh_private_key_file: key/deploy # Path to your encrypted key file tasks: # Your decryption and deployment tasks here ``` ### Why `gather_facts: false` is Mandatory **Without `gather_facts: false`:** - Ansible attempts to connect to target hosts immediately - It tries to use SSH with the default SSH agent configuration - Fails because the encrypted key isn't decrypted yet - Playbook stops before reaching your decryption task **With `gather_facts: false`:** - Ansible skips the initial fact gathering phase - Allows your decryption task to run first - You control when and how the SSH key is used ## Step 1: Encrypt SSH Private Key with Ansible Vault ### Encrypt an existing SSH key: ```bash # Method 1: Interactive password prompt ansible-vault encrypt key/deploy --ask-vault-pass # Method 2: Using a password file ansible-vault encrypt key/deploy --vault-password-file vault_pass.txt # Method 3: Using vault ID ansible-vault encrypt key/deploy --vault-id deploy@vault_pass.txt ``` ### Create a new encrypted SSH key: ```bash ansible-vault create key/deploy --vault-password-file vault_pass.txt ``` ### Verify encrypted content: ```bash ansible-vault view key/deploy --vault-password-file vault_pass.txt ``` ## Step 2: Complete Ansible Playbook Example ### Full playbook structure: ```yaml --- - name: Deploy using encrypted SSH key hosts: all gather_facts: false # CRITICAL: Must be disabled vars: ssh_private_key_file: key/deploy decrypted_key_path: /tmp/ansible_deploy_key tasks: - name: Decrypt SSH private key copy: src: "{{ ssh_private_key_file }}" dest: "{{ decrypted_key_path }}" decrypt: yes mode: '0600' delegate_to: localhost become: false run_once: true - name: Enable fact gathering with decrypted key setup: delegate_to: localhost become: false - name: Use the decrypted key for deployment ansible.builtin.shell: | ssh -i "{{ decrypted_key_path }}" \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ deploy@{{ inventory_hostname }} 'deployment_command' args: executable: /bin/bash - name: Remove decrypted SSH key (cleanup) file: path: "{{ decrypted_key_path }}" state: absent delegate_to: localhost become: false always: yes ``` ## Step 3: Running the Playbook ### Execution methods: ```bash # With password file ansible-playbook playbook.yml --vault-password-file vault_pass.txt # Interactive password prompt ansible-playbook playbook.yml --ask-vault-pass # With vault ID ansible-playbook playbook.yml --vault-id deploy@vault_pass.txt # With inventory file ansible-playbook -i hosts.ini playbook.yml --vault-password-file vault_pass.txt ``` ## Step 4: Advanced Error Handling & Security ### Robust playbook with proper error handling: ```yaml --- - name: Secure deployment with encrypted SSH key hosts: all gather_facts: false vars: ssh_private_key_file: key/deploy decrypted_key_path: "/tmp/ansible_deploy_key_{{ ansible_date_time.epoch }}" tasks: - name: Ensure encrypted key file exists stat: path: "{{ ssh_private_key_file }}" register: key_file delegate_to: localhost become: false - name: Fail if encrypted key is missing fail: msg: "Encrypted SSH key file {{ ssh_private_key_file }} not found" when: not key_file.stat.exists delegate_to: localhost become: false - name: Decrypt SSH private key copy: src: "{{ ssh_private_key_file }}" dest: "{{ decrypted_key_path }}" decrypt: yes mode: '0600' delegate_to: localhost become: false run_once: true - name: Verify decrypted key permissions file: path: "{{ decrypted_key_path }}" mode: '0600' delegate_to: localhost become: false - name: Gather facts using decrypted key (if needed) setup: delegate_to: localhost become: false - name: Perform deployment tasks block: - name: Execute remote deployment ansible.builtin.shell: | ssh -i "{{ decrypted_key_path }}" \ -o ConnectTimeout=30 \ -o StrictHostKeyChecking=no \ deploy@{{ inventory_hostname }} 'your_deployment_script' args: executable: /bin/bash register: deployment_result - name: Display deployment output debug: var: deployment_result.stdout rescue: - name: Handle deployment failure debug: msg: "Deployment failed - check SSH connectivity and permissions" always: - name: Always remove decrypted key file: path: "{{ decrypted_key_path }}" state: absent delegate_to: localhost become: false ``` ## Security Best Practices ### 1. File Security: ```bash # Secure permissions for password files chmod 600 vault_pass.txt # Secure permissions for encrypted key chmod 600 key/deploy ``` ### 2. Temporary File Safety: - Use unique temporary filenames with timestamps - Set strict permissions (0600) - Always clean up, even on failure ### 3. Key Management: - Never store unencrypted keys in version control - Rotate deployment keys regularly - Use different keys for different environments ## Troubleshooting ### Common Issues & Solutions: 1. **"Permission denied" errors:** - Verify vault password is correct - Check encrypted file permissions - Ensure cleanup tasks run successfully 2. **SSH connection failures:** - Verify the decrypted key is authorized on target hosts - Check network connectivity - Validate target host accessibility 3. **Fact gathering issues:** - Use `gather_facts: false` in main playbook - Manually call `setup` module after decryption if needed ### Debug Commands: ```bash # Test vault decryption ansible-vault view key/deploy --vault-password-file vault_pass.txt # Verify playbook syntax ansible-playbook playbook.yml --syntax-check # Dry run to see what would happen ansible-playbook playbook.yml --vault-password-file vault_pass.txt --check ``` ## Summary The key points for successful encrypted SSH key management: 1. **Always use `gather_facts: false`** in the main playbook 2. **Decrypt the key early** in your tasks 3. **Use unique temporary paths** for decrypted keys 4. **Always clean up** decrypted keys, even on failures 5. **Secure your vault passwords** with proper file permissions This approach ensures your SSH keys remain encrypted at rest and are only temporarily decrypted during execution, maintaining security throughout your deployment process.