- name: Check name of gpg2 binary command: which gpg2 register: gpg2_check ignore_errors: true - name: Set gpg2 binary name set_fact: gpg2_bin: '{{ "gpg2" if gpg2_check is successful else "gpg" }}' - name: Stop gpg-agent so we can remove any locks on the GnuPG dir command: gpgconf --kill gpg-agent ignore_errors: yes - name: Remove previous password files and directory file: dest: "{{ item }}" state: absent loop: - "~/.gnupg" - "~/.password-store" - name: Get path of pass executable command: which pass register: result - name: Store path of pass executable set_fact: passpath: "{{ result.stdout }}" - name: Move original pass into place if there was a leftover command: argv: - mv - "{{ passpath }}.testorig" - "{{ passpath }}" args: removes: "{{ passpath }}.testorig" # having gopass is not required for this test, but we store # its path in case it is installed, so we can restore it - name: Try to find gopass in path command: which gopass register: result ignore_errors: yes - name: Store path of gopass executable set_fact: gopasspath: "{{ (result.rc == 0) | ternary(result.stdout, (passpath | dirname, 'gopass') | path_join) }}" - name: Move original gopass into place if there was a leftover command: argv: - mv - "{{ gopasspath }}.testorig" - "{{ gopasspath }}" args: removes: "{{ gopasspath }}.testorig" # How to generate a new GPG key: # gpg2 --batch --gen-key input # See templates/input # gpg2 --list-secret-keys --keyid-format LONG # gpg2 --armor --export-secret-keys [key id] # # Get the fingerprint # gpg2 --fingerprint --keyid-format LONG | grep [key id] -A 1 | tail -1 | tr -d '[:space:]' | awk -F '=' '{print $2":6:"}' - name: Import GPG private key shell: echo "{{ passwordstore_privkey }}" | {{ gpg2_bin }} --import --allow-secret-key-import - - name: Trust key shell: echo "D3E1CC8934E97270CEB066023AF1BD3619AB496A:6:" | {{ gpg2_bin }} --import-ownertrust - name: Initialise passwordstore command: pass init ansible-test - name: Create a password set_fact: newpass: "{{ lookup('community.general.passwordstore', 'test-pass length=8 create=yes') }}" - name: Fetch password from an existing file set_fact: readpass: "{{ lookup('community.general.passwordstore', 'test-pass') }}" - name: Verify password assert: that: - readpass == newpass - name: Create a password with equal sign set_fact: newpass: "{{ lookup('community.general.passwordstore', 'test-pass-equal userpass=SimpleSample= create=yes') }}" - name: Fetch a password with equal sign set_fact: readpass: "{{ lookup('community.general.passwordstore', 'test-pass-equal') }}" - name: Verify password assert: that: - readpass == newpass - name: Create a password using missing=create set_fact: newpass: "{{ lookup('community.general.passwordstore', 'test-missing-create missing=create length=8') }}" - name: Fetch password from an existing file set_fact: readpass: "{{ lookup('community.general.passwordstore', 'test-missing-create') }}" - name: Verify password assert: that: - readpass == newpass - name: Fetch password from existing file using missing=empty set_fact: readpass: "{{ lookup('community.general.passwordstore', 'test-missing-create missing=empty') }}" - name: Verify password assert: that: - readpass == newpass - name: Fetch password from non-existing file using missing=empty set_fact: readpass: "{{ query('community.general.passwordstore', 'test-missing-pass missing=empty') }}" - name: Verify password assert: that: - readpass == [ none ] # As inserting multiline passwords on the commandline would require something # like expect, simply create it by using default gpg on a file with the correct # structure. - name: Create the YAML password content copy: dest: "~/.password-store/test-yaml-pass" content: | testpassword key: | multi line - name: Read .gpg-id from .password-store set_fact: gpgid: "{{ lookup('file', '~/.password-store/.gpg-id') }}" - name: Encrypt the file using the gpg key command: "{{ gpg2_bin }} --batch --encrypt -r {{ gpgid }} ~/.password-store/test-yaml-pass" - name: Fetch a password with YAML subkey set_fact: readyamlpass: "{{ lookup('community.general.passwordstore', 'test-yaml-pass subkey=key') }}" - name: Read a yaml subkey assert: that: - readyamlpass == 'multi\nline' - name: Create a non-YAML multiline file copy: dest: "~/.password-store/test-multiline-pass" content: | testpassword random additional line - name: Read .gpg-id from .password-store set_fact: gpgid: "{{ lookup('file', '~/.password-store/.gpg-id') }}" - name: Encrypt the file using the gpg key command: "{{ gpg2_bin }} --batch --encrypt -r {{ gpgid }} ~/.password-store/test-multiline-pass" - name: Fetch password from multiline file set_fact: readyamlpass: "{{ lookup('community.general.passwordstore', 'test-multiline-pass') }}" - name: Multiline pass only returns first line assert: that: - readyamlpass == 'testpassword' - name: Fetch all from multiline file set_fact: readyamlpass: "{{ lookup('community.general.passwordstore', 'test-multiline-pass returnall=yes') }}" - name: Multiline pass returnall returns everything in the file assert: that: - readyamlpass == 'testpassword\nrandom additional line' - name: Create a password in a folder set_fact: newpass: "{{ lookup('community.general.passwordstore', 'folder/test-pass length=8 create=yes') }}" - name: Fetch password from folder set_fact: readpass: "{{ lookup('community.general.passwordstore', 'folder/test-pass') }}" - name: Verify password from folder assert: that: - readpass == newpass - name: Try to read folder as passname set_fact: newpass: "{{ lookup('community.general.passwordstore', 'folder') }}" ignore_errors: true register: eval_error - name: Make sure reading folder as passname failed assert: that: - eval_error is failed - '"passname folder not found" in eval_error.msg' - name: Change passwordstore location explicitly set_fact: passwordstore: "{{ lookup('env','HOME') }}/.password-store" - name: Make sure password store still works with explicit location set set_fact: newpass: "{{ lookup('community.general.passwordstore', 'test-pass') }}" - name: Change passwordstore location to a non-existent place set_fact: passwordstore: "somenonexistentplace" - name: Try reading from non-existent passwordstore location set_fact: newpass: "{{ lookup('community.general.passwordstore', 'test-pass') }}" ignore_errors: true register: eval_error - name: Make sure reading from non-existent passwordstore location failed assert: that: - eval_error is failed - >- "Passwordstore directory 'somenonexistentplace' does not exist" in eval_error.msg - name: Test pass compatibility shim detection block: - name: Move original pass out of the way command: argv: - mv - "{{ passpath }}" - "{{ passpath }}.testorig" args: creates: "{{ passpath }}.testorig" - name: Create dummy pass script ansible.builtin.copy: content: | #!/bin/sh echo "shim_ok" dest: "{{ passpath }}" mode: '0755' - name: Try reading from non-existent passwordstore location with different pass utility set_fact: newpass: "{{ lookup('community.general.passwordstore', 'test-pass') }}" environment: PATH: "/tmp" - name: Verify password received from shim assert: that: - newpass == "shim_ok" - name: Try to read folder as passname with a different pass utility set_fact: newpass: "{{ lookup('community.general.passwordstore', 'folder') }}" - name: Verify password received from shim assert: that: - newpass == "shim_ok" always: - name: Move original pass back into place command: argv: - mv - "{{ passpath }}.testorig" - "{{ passpath }}" args: removes: "{{ passpath }}.testorig" - name: Very basic gopass compatibility test vars: passwordstore_backend: "gopass" block: - name: check if gopass executable exists stat: path: "{{ gopasspath }}" register: gopass_check - name: Move original gopass out of the way command: argv: - mv - "{{ gopasspath }}" - "{{ gopasspath }}.testorig" args: creates: "{{ gopasspath }}.testorig" when: gopass_check.stat.exists == true - name: Create mocked gopass script ansible.builtin.copy: content: | #!/bin/sh if [ "$GOPASS_NO_REMINDER" != "YES" ]; then exit 1 fi if [ "$1" = "--version" ]; then exit 2 fi echo "gopass_ok" dest: "{{ gopasspath }}" mode: '0755' - name: Try to read folder as passname using gopass set_fact: newpass: "{{ lookup('community.general.passwordstore', 'folder') }}" - name: Verify password received from gopass assert: that: - newpass == "gopass_ok" always: - name: Remove mocked gopass ansible.builtin.file: path: "{{ gopasspath }}" state: absent - name: Move original gopass back into place command: argv: - mv - "{{ gopasspath }}.testorig" - "{{ gopasspath }}" args: removes: "{{ gopasspath }}.testorig" when: gopass_check.stat.exists == true