--- # ========================================================== # 1️⃣ Install Vault and Dependencies # ========================================================== - name: Ensure HashiCorp repository (RHEL9) ansible.builtin.copy: dest: /etc/yum.repos.d/hashicorp.repo mode: "0644" content: | [hashicorp] name=HashiCorp Stable Repository baseurl=https://rpm.releases.hashicorp.com/RHEL/9/x86_64/stable/ enabled=1 gpgcheck=1 gpgkey=https://rpm.releases.hashicorp.com/gpg - name: Install Vault (includes vault-agent) ansible.builtin.dnf: name: vault state: present # ========================================================== # 2️⃣ Firewall Configuration # ========================================================== - name: Ensure firewalld is running and enabled ansible.builtin.systemd: name: firewalld enabled: true state: started - name: Allow outbound access to Vault (VAULT_PORT/tcp) ansible.posix.firewalld: rich_rule: > rule family="ipv4" destination address="VAULT_IP" port port="VAULT_PORT" protocol="tcp" accept permanent: true state: enabled immediate: true # ========================================================== # 3️⃣ Vault-Agent Directories and Configs # ========================================================== - name: Ensure Vault-Agent directories exist ansible.builtin.file: path: "{{ vault_dir.path }}" state: directory owner: root group: root mode: "{{ vault_dir.mode | default('0750') }}" loop: - { path: "{{ vault_templates_dir }}" } - { path: "/etc/vault-agent", mode: "0700" } loop_control: loop_var: vault_dir - name: Deploy Vault-Agent configuration ansible.builtin.template: src: vault-agent-config.hcl.j2 dest: "{{ vault_agent_config_path }}" owner: root group: root mode: "0600" - name: Install Vault-Agent systemd unit ansible.builtin.template: src: vault-agent.service.j2 dest: "{{ vault_agent_systemd_unit }}" owner: root group: root mode: "0644" notify: Reload systemd - name: Enable Vault-Agent but do not start yet ansible.builtin.systemd: name: "{{ vault_agent_service }}" enabled: true state: stopped # ========================================================== # 4️⃣ Vault-Agent Templates and Cert Paths # ========================================================== - name: Copy Vault-Agent templates ansible.builtin.copy: src: "{{ vault_item.src }}" dest: "{{ vault_templates_dir }}/{{ vault_item.src | basename }}" owner: root group: root mode: "0600" loop: "{{ vault_agent_templates }}" loop_control: loop_var: vault_item - name: Ensure directories for Vault-rendered files exist ansible.builtin.file: path: "{{ vault_template.dest | dirname }}" state: directory owner: root group: root mode: "0755" loop: "{{ vault_agent_templates }}" loop_control: loop_var: vault_template - name: Start Vault-Agent (now that templates exist) ansible.builtin.systemd: name: "{{ vault_agent_service }}" state: started # ========================================================== # 5️⃣ AppRole Authentication Setup # ========================================================== - name: Fetch AppRole credentials for this host delegate_to: localhost block: - name: Get role_id ansible.builtin.command: cmd: > vault read -field=role_id auth/approle/role/{{ inventory_hostname }}-role/role-id register: role_id_raw changed_when: false failed_when: role_id_raw.stdout | trim == "" environment: VAULT_ADDR: "http://VAULT_IP:VAULT_PORT" VAULT_TOKEN: "{{ vault_token }}" - name: Get secret_id ansible.builtin.command: cmd: > vault write -f -field=secret_id auth/approle/role/{{ inventory_hostname }}-role/secret-id register: secret_id_raw changed_when: false failed_when: secret_id_raw.stdout | trim == "" environment: VAULT_ADDR: "http://VAULT_IP:VAULT_PORT" VAULT_TOKEN: "{{ vault_token }}" - name: Write AppRole credentials to Vault-Agent directory ansible.builtin.copy: dest: "/etc/vault-agent/{{ cred.name }}" content: "{{ cred.content }}" owner: root group: root mode: "0600" loop: - { name: "role_id", content: "{{ role_id_raw.stdout | trim }}" } - { name: "secret_id", content: "{{ secret_id_raw.stdout | trim }}" } loop_control: loop_var: cred label: "{{ cred.name }}" # ========================================================== # 6️⃣ Register Service in Vault # ========================================================== - name: Login to Vault with AppRole and register VM delegate_to: localhost block: - name: Login to Vault with AppRole ansible.builtin.command: cmd: > vault write -field=token auth/approle/login role_id={{ role_id_raw.stdout | trim }} secret_id={{ secret_id_raw.stdout | trim }} register: vault_login changed_when: false environment: VAULT_ADDR: "http://VAULT_IP:VAULT_PORT" - name: Register VM IP in Vault KV ansible.builtin.uri: url: "http://VAULT_IP:VAULT_PORT/v1/kv/data/services/{{ inventory_hostname }}" method: POST headers: X-Vault-Token: "{{ vault_login.stdout }}" body: data: ip: "{{ ansible_default_ipv4.address }}" body_format: json status_code: [200, 204] # ========================================================== # 7️⃣ SELinux Context for Vault Rendered Paths # ========================================================== - name: Check if SELinux is enforcing ansible.builtin.command: getenforce register: selinux_status changed_when: false failed_when: false - name: Gather all Vault-Agent template and rendered paths ansible.builtin.set_fact: all_vault_paths: >- {{ ( (vault_agent_templates | default([]) | map(attribute='dest') | map('dirname') | list) + (vault_agent_templates | default([]) | map(attribute='dest') | list) ) | unique }} when: vault_agent_templates is defined and vault_agent_templates | length > 0 - name: Filter only paths that exist on the host ansible.builtin.find: paths: "{{ all_vault_paths }}" file_type: directory register: existing_selinux_paths when: all_vault_paths is defined and all_vault_paths | length > 0 - name: Apply SELinux fcontext to all existing Vault paths ansible.builtin.command: cmd: semanage fcontext -a -t etc_t '{{ selinux_path_item.path }}(/.*)?' loop: "{{ existing_selinux_paths.files | default([]) }}" loop_control: loop_var: selinux_path_item ignore_errors: true when: selinux_status.stdout == "Enforcing" - name: Restore SELinux context for all existing Vault paths ansible.builtin.command: cmd: restorecon -Rv "{{ selinux_path_item.path }}" loop: "{{ existing_selinux_paths.files | default([]) }}" loop_control: loop_var: selinux_path_item ignore_errors: true when: selinux_status.stdout == "Enforcing" - name: Warn if SELinux is not enforcing ansible.builtin.debug: msg: "⚠️ SELinux is not enforcing — Vault-Agent directories may not be properly confined." when: selinux_status.stdout != "Enforcing"