From 36eb0caa8c8e0d68f0bbb3e1482ab129825cd445 Mon Sep 17 00:00:00 2001 From: Lin Gao Date: Wed, 6 Dec 2023 21:53:49 +0800 Subject: [PATCH 1/3] [SET-651] Adds a vault Ansible role to retrieve HashiCorp Vault secrets and convert to variables It supports both token and approle authentication to fit different scenarios --- requirements.yml | 1 + roles/vault/defaults/main.yml | 8 ++++++++ roles/vault/tasks/main.yml | 25 +++++++++++++++++++++++++ roles/vault/tasks/vault-approle.yml | 29 +++++++++++++++++++++++++++++ roles/vault/tasks/vault-token.yml | 27 +++++++++++++++++++++++++++ 5 files changed, 90 insertions(+) create mode 100644 roles/vault/defaults/main.yml create mode 100644 roles/vault/tasks/main.yml create mode 100644 roles/vault/tasks/vault-approle.yml create mode 100644 roles/vault/tasks/vault-token.yml diff --git a/requirements.yml b/requirements.yml index 13ec0b41..6decf2f0 100644 --- a/requirements.yml +++ b/requirements.yml @@ -3,5 +3,6 @@ collections: - community.general - community.crypto - community.postgresql + - community.hashi_vault - ansible.posix - containers.podman diff --git a/roles/vault/defaults/main.yml b/roles/vault/defaults/main.yml new file mode 100644 index 00000000..24e9d7da --- /dev/null +++ b/roles/vault/defaults/main.yml @@ -0,0 +1,8 @@ +--- +ansible_hashi_vault_addr: "{{ lookup('ansible.builtin.env', 'VAULT_ADDR') }}" +ansible_hashi_vault_token: "{{ lookup('ansible.builtin.env', 'VAULT_TOKEN') }}" +ansible_hashi_vault_role_id: "{{ lookup('ansible.builtin.env', 'ANSIBLE_HASHI_VAULT_ROLE_ID') }}" +ansible_hashi_vault_secret_id: "{{ lookup('ansible.builtin.env', 'ANSIBLE_HASHI_VAULT_SECRET_ID') }}" +vault_mount: 'secret' +vault_paths: + - "jboss-set" diff --git a/roles/vault/tasks/main.yml b/roles/vault/tasks/main.yml new file mode 100644 index 00000000..4d68849f --- /dev/null +++ b/roles/vault/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- ansible.builtin.assert: + that: + - vault_paths is defined + - vault_paths | length > 0 + - ansible_hashi_vault_addr is defined and ansible_hashi_vault_addr | length > 0 + quiet: true + +- name: "Check the Vault Secrets using token auth method" + ansible.builtin.include_tasks: tasks/vault-token.yml + with_items: "{{ vault_paths }}" + loop_control: + loop_var: vault_path + when: + - ansible_hashi_vault_token is defined and ansible_hashi_vault_token | length > 0 + +- name: "Check the Vault Secrets using approle auth method" + ansible.builtin.include_tasks: tasks/vault-approle.yml + with_items: "{{ vault_paths }}" + loop_control: + loop_var: vault_path + when: + - ansible_hashi_vault_token is not defined or ansible_hashi_vault_token | length == 0 + - ansible_hashi_vault_role_id is defined and ansible_hashi_vault_role_id | length > 0 + - ansible_hashi_vault_secret_id is defined and ansible_hashi_vault_secret_id | length > 0 \ No newline at end of file diff --git a/roles/vault/tasks/vault-approle.yml b/roles/vault/tasks/vault-approle.yml new file mode 100644 index 00000000..f3d27a99 --- /dev/null +++ b/roles/vault/tasks/vault-approle.yml @@ -0,0 +1,29 @@ +--- +- ansible.builtin.assert: + that: + - vault_path is defined + - ansible_hashi_vault_addr is defined and ansible_hashi_vault_addr | length > 0 + - ansible_hashi_vault_role_id is defined and ansible_hashi_vault_role_id | length > 0 + - ansible_hashi_vault_secret_id is defined and ansible_hashi_vault_secret_id | length > 0 + quiet: true + +- name: "Retrieve the secrets from Vault Path {{ vault_path }} using approle" + community.hashi_vault.vault_kv2_get: + url: '{{ ansible_hashi_vault_addr }}' + auth_method: approle + role_id: '{{ ansible_hashi_vault_role_id }}' + secret_id: '{{ ansible_hashi_vault_secret_id }}' + engine_mount_point: '{{ vault_mount }}' + path: '{{ vault_path }}' + register: vault_path_result + +- name: "Set variables from result of vault path {{ vault_path }}" + ansible.builtin.set_fact: + "{{ item.key }}": "{{ item.value }}" + with_items: "{{ vault_path_result.secret | dict2items }}" + loop_control: + label: "{{ item.key }} ==> ***** " + when: + - vault_path_result is defined + - vault_path_result.secret is defined and vault_path_result.secret | length > 0 + diff --git a/roles/vault/tasks/vault-token.yml b/roles/vault/tasks/vault-token.yml new file mode 100644 index 00000000..33a33fbf --- /dev/null +++ b/roles/vault/tasks/vault-token.yml @@ -0,0 +1,27 @@ +--- +- ansible.builtin.assert: + that: + - vault_path is defined + - ansible_hashi_vault_addr is defined and ansible_hashi_vault_addr | length > 0 + - ansible_hashi_vault_token is defined and ansible_hashi_vault_token | length > 0 + quiet: true + +- name: "Retrieve the secrets from Vault Path {{ vault_path }} using token auth method" + community.hashi_vault.vault_kv2_get: + url: '{{ ansible_hashi_vault_addr }}' + auth_method: token + token: '{{ ansible_hashi_vault_token }}' + engine_mount_point: '{{ vault_mount }}' + path: '{{ vault_path }}' + register: vault_path_result + +- name: "Set variables from result of vault path {{ vault_path }}" + ansible.builtin.set_fact: + "{{ item.key }}": "{{ item.value }}" + with_items: "{{ vault_path_result.secret | dict2items }}" + loop_control: + label: "{{ item.key }} ==> ***** " + when: + - vault_path_result is defined + - vault_path_result.secret is defined and vault_path_result.secret | length > 0 + From 31209b1639f513e3b246710a655df674b7fa6be4 Mon Sep 17 00:00:00 2001 From: Lin Gao Date: Wed, 13 Dec 2023 09:55:43 +0800 Subject: [PATCH 2/3] [SET-651] Adds the ability to copy ssh keys content instead of coping from file --- roles/ssh/tasks/deploy.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/roles/ssh/tasks/deploy.yml b/roles/ssh/tasks/deploy.yml index 69d855bf..d8412582 100644 --- a/roles/ssh/tasks/deploy.yml +++ b/roles/ssh/tasks/deploy.yml @@ -31,6 +31,21 @@ - files.ssh_keys is defined - files.ssh_keys | length > 0 +- name: "Deploy identity content into {{ ssh_home }}" + ansible.builtin.copy: + content: "{{ key.content }}" + dest: "{{ ssh_home }}/.ssh/{{ key.file_name }}" + owner: "{{ files.username }}" + group: "{{ group }}" + mode: 0400 + loop: "{{ files.ssh_keys_content }}" + loop_control: + loop_var: key + when: + - files.ssh_keys is not defined + - files.ssh_keys_content is defined + - files.ssh_keys_content | length > 0 + - block: - ansible.builtin.assert: that: From 6c0120dcedcf45031da1eef6522f5627611924be Mon Sep 17 00:00:00 2001 From: Lin Gao Date: Thu, 7 Dec 2023 10:33:24 +0800 Subject: [PATCH 3/3] [SET-651] Adds molecule tests for vault role --- molecule/vault/converge.yml | 8 ++++ molecule/vault/molecule.yml | 44 +++++++++++++++++ molecule/vault/prepare.yml | 94 +++++++++++++++++++++++++++++++++++++ molecule/vault/vars.yml | 19 ++++++++ molecule/vault/vault.yml | 26 ++++++++++ molecule/vault/verify.yml | 20 ++++++++ 6 files changed, 211 insertions(+) create mode 100644 molecule/vault/converge.yml create mode 100644 molecule/vault/molecule.yml create mode 100644 molecule/vault/prepare.yml create mode 100644 molecule/vault/vars.yml create mode 100644 molecule/vault/vault.yml create mode 100644 molecule/vault/verify.yml diff --git a/molecule/vault/converge.yml b/molecule/vault/converge.yml new file mode 100644 index 00000000..9a443a68 --- /dev/null +++ b/molecule/vault/converge.yml @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + vars_files: + - vars.yml + tasks: + - name: "Include vault tasks" + ansible.builtin.include_tasks: vault.yml diff --git a/molecule/vault/molecule.yml b/molecule/vault/molecule.yml new file mode 100644 index 00000000..7e01fc7a --- /dev/null +++ b/molecule/vault/molecule.yml @@ -0,0 +1,44 @@ +--- +dependency: + name: galaxy + options: + ignore-certs: True + ignore-errors: True + requirements-file: molecule/default/requirements.yml +driver: + name: podman +platforms: + - name: instance + image: registry.access.redhat.com/ubi8/ubi-init + tmpfs: + - /run + - /tmp + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + capabilities: + - SYS_ADMIN + - CAP_IPC_LOCK + command: "/usr/sbin/init" + pre_build_image: true +provisioner: + name: ansible + playbooks: + converge: converge.yml + verify: verify.yml + env: + ANSIBLE_ROLES_PATH: "../../roles" +verifier: + name: ansible +scenario: + test_sequence: + - dependency + - cleanup + - destroy + - create + - prepare + - converge + - idempotence + - side_effect + - verify + - cleanup + - destroy diff --git a/molecule/vault/prepare.yml b/molecule/vault/prepare.yml new file mode 100644 index 00000000..513ad513 --- /dev/null +++ b/molecule/vault/prepare.yml @@ -0,0 +1,94 @@ +--- +- name: Prepare + hosts: all + vars_files: + - vars.yml + environment: + - VAULT_ADDR: "{{ ansible_hashi_vault_addr }}" + tasks: + - name: "Import Vault repo for installation" + ansible.builtin.yum_repository: + name: 'vault' + description: 'The vault repository' + file: external_repos + baseurl: https://rpm.releases.hashicorp.com/RHEL/$releasever/$basearch/stable + gpgcheck: no + + - name: "Install Vault and pip" + ansible.builtin.package: + name: "{{ item }}" + state: present + with_items: + - vault + - python3-pip + + - name: Install hvac python package + ansible.builtin.pip: + name: hvac + + - name: "Set up Vault env - Start dev server in background for 5 minutes" + ansible.builtin.shell: "vault server -dev -dev-root-token-id={{ root_vault_token }}" + async: 300 + poll: 0 + register: result + + - name: "Show the Vault server start result" + ansible.builtin.debug: + msg: "{{ result }}" + + - name: Create a temporary shell script file + ansible.builtin.copy: + content: | + #!/bin/bash + set -x + sleep 5 + echo "Create test policy to read on path: secret/*" + echo 'path "secret/*" { + capabilities = ["list", "read"] + }' | vault policy write test - + echo "Enable approle auth method" + vault auth enable approle + echo "Set up the role_id " + vault write auth/approle/role/{{ role_name }} secret_id_ttl=10m token_num_uses=10 token_ttl=20m token_max_ttl=30m secret_id_num_uses=40 policies="default","test" + dest: /tmp/setup_vault.sh + mode: '0755' + + - name: Execute the script to set up Vault + command: '/tmp/setup_vault.sh' + register: script_output + + - name: Display script output for debugging purpose + debug: + var: script_output.stdout_lines + + - name: Remove the temporary script file + file: + path: /tmp/setup_vault.sh + state: absent + + - name: "Set up Vault env - Write test data" + shell: | + vault kv put {{ vault_mount }}/{{ item.vault_path }} {{ item.secrets }} + with_items: "{{ test_secrets }}" + + - name: "Read role_id for role: {{ role_name }} " + community.hashi_vault.vault_read: + path: "auth/approle/role/{{ role_name }}/role-id" + register: role_id + + - name: Generate a secret-id for the given approle + community.hashi_vault.vault_write: + path: "auth/approle/role/{{ role_name }}/secret-id" + register: secret_id + + - name: "Write role_id to {{ role_id_file }}" + ansible.builtin.copy: + content: "{{ role_id.data.data.role_id }}" + dest: "{{ role_id_file }}" + mode: '0644' + + - name: "Write secret_id to {{ secret_id_file }}" + ansible.builtin.copy: + content: "{{ secret_id.data.data.secret_id }}" + dest: "{{ secret_id_file }}" + mode: '0644' \ No newline at end of file diff --git a/molecule/vault/vars.yml b/molecule/vault/vars.yml new file mode 100644 index 00000000..78283c06 --- /dev/null +++ b/molecule/vault/vars.yml @@ -0,0 +1,19 @@ +--- +ansible_hashi_vault_addr: 'http://127.0.0.1:8200' +root_vault_token: 'root-token' +role_name: 'jboss-set' +vault_mount: "secret" +vault_paths: + - "jboss-set/ansible" + - "jboss-set/olympus" +test_secrets: + - { + "vault_path": "jboss-set/ansible", + "secrets": "ansible_site=ansible.com ansible_nodename=node1" + } + - { + "vault_path": "jboss-set/olympus", + "secrets": "olympus_ip=127.0.0.1 olympus_fqdn=olympus.host.local" + } +role_id_file: '/tmp/role_id' +secret_id_file: '/tmp/secret_id' \ No newline at end of file diff --git a/molecule/vault/vault.yml b/molecule/vault/vault.yml new file mode 100644 index 00000000..f926af87 --- /dev/null +++ b/molecule/vault/vault.yml @@ -0,0 +1,26 @@ +--- +- name: "Read role_id from {{ role_id_file }}" + ansible.builtin.slurp: + src: "{{ role_id_file }}" + register: role_id + +- name: "Set role_id variable" + ansible.builtin.set_fact: + "ansible_hashi_vault_role_id": "{{ role_id.content | b64decode }}" + +- name: "Read secret_id from {{ secret_id_file }}" + ansible.builtin.slurp: + src: "{{ secret_id_file }}" + register: secret_id + +- name: "Set secret_id varialbe" + ansible.builtin.set_fact: + "ansible_hashi_vault_secret_id": "{{ secret_id.content | b64decode }}" + +- name: "Show the role_id and secret_id" + ansible.builtin.debug: + msg: "role_id: {{ ansible_hashi_vault_role_id }}, secret_id: {{ ansible_hashi_vault_secret_id }}" + +- name: "Include vault" + ansible.builtin.include_role: + name: "vault" diff --git a/molecule/vault/verify.yml b/molecule/vault/verify.yml new file mode 100644 index 00000000..013863c1 --- /dev/null +++ b/molecule/vault/verify.yml @@ -0,0 +1,20 @@ +--- +- name: Verify for vault role + hosts: all + vars_files: + - vars.yml + gather_facts: false + + tasks: + - name: "Include vault tasks" + ansible.builtin.include_tasks: vault.yml + + - name: Check variables + ansible.builtin.assert: + that: + - ansible_site == 'ansible.com' + - ansible_nodename == 'node1' + - olympus_ip == '127.0.0.1' + - olympus_fqdn == 'olympus.host.local' + fail_msg: "Secrets in Vault should be converted to Anislbe variables" + success_msg: "Great, it passed!"