diff --git a/lib/ansible/module_utils/crypto.py b/lib/ansible/module_utils/crypto.py index 711e7b856d7..3d51d064bd3 100644 --- a/lib/ansible/module_utils/crypto.py +++ b/lib/ansible/module_utils/crypto.py @@ -29,6 +29,7 @@ import hashlib import os from ansible.module_utils import six +from ansible.module_utils._text import to_bytes class OpenSSLObjectError(Exception): @@ -63,7 +64,7 @@ def load_privatekey(path, passphrase=None): if passphrase: privatekey = crypto.load_privatekey(crypto.FILETYPE_PEM, open(path, 'rb').read(), - passphrase) + to_bytes(passphrase)) else: privatekey = crypto.load_privatekey(crypto.FILETYPE_PEM, open(path, 'rb').read()) diff --git a/lib/ansible/modules/crypto/openssl_privatekey.py b/lib/ansible/modules/crypto/openssl_privatekey.py index 75daa03cc3c..a0ed0d6debe 100644 --- a/lib/ansible/modules/crypto/openssl_privatekey.py +++ b/lib/ansible/modules/crypto/openssl_privatekey.py @@ -134,7 +134,7 @@ else: pyopenssl_found = True from ansible.module_utils import crypto as crypto_utils -from ansible.module_utils._text import to_native +from ansible.module_utils._text import to_native, to_bytes from ansible.module_utils.basic import AnsibleModule @@ -181,11 +181,11 @@ class PrivateKey(crypto_utils.OpenSSLObject): os.O_WRONLY | os.O_CREAT | os.O_TRUNC, self.mode) - extras = {} if self.cipher and self.passphrase: - extras = {'cipher': self.cipher, 'passphrase': self.passphrase} - - os.write(privatekey_file, crypto.dump_privatekey(crypto.FILETYPE_PEM, self.privatekey, **extras)) + os.write(privatekey_file, crypto.dump_privatekey(crypto.FILETYPE_PEM, self.privatekey, + self.cipher, to_bytes(self.passphrase))) + else: + os.write(privatekey_file, crypto.dump_privatekey(crypto.FILETYPE_PEM, self.privatekey)) os.close(privatekey_file) self.changed = True except IOError as exc: diff --git a/test/integration/targets/openssl_privatekey/tasks/main.yml b/test/integration/targets/openssl_privatekey/tasks/main.yml index 464f56e17e3..d33134baddc 100644 --- a/test/integration/targets/openssl_privatekey/tasks/main.yml +++ b/test/integration/targets/openssl_privatekey/tasks/main.yml @@ -12,4 +12,32 @@ path: '{{ output_dir }}/privatekey3.pem' type: DSA +- name: Generate privatekey4 - standard + openssl_privatekey: + path: '{{ output_dir }}/privatekey4.pem' + +- name: Delete privatekey4 - standard + openssl_privatekey: + state: absent + path: '{{ output_dir }}/privatekey4.pem' + +- name: Generate privatekey5 - standard - with passphrase + openssl_privatekey: + path: '{{ output_dir }}/privatekey5.pem' + passphrase: ansible + cipher: aes256 + +- name: Generate privatekey5 - standard - idempotence + openssl_privatekey: + path: '{{ output_dir }}/privatekey5.pem' + passphrase: ansible + cipher: aes256 + register: privatekey5_idempotence + +- name: Generate privatekey6 - standard - with non-ASCII passphrase + openssl_privatekey: + path: '{{ output_dir }}/privatekey6.pem' + passphrase: ànsïblé + cipher: aes256 + - import_tasks: ../tests/validate.yml diff --git a/test/integration/targets/openssl_privatekey/tests/validate.yml b/test/integration/targets/openssl_privatekey/tests/validate.yml index ccbce924e55..b2e9d4789eb 100644 --- a/test/integration/targets/openssl_privatekey/tests/validate.yml +++ b/test/integration/targets/openssl_privatekey/tests/validate.yml @@ -1,28 +1,70 @@ -- name: Validate privatekey1 (test) +- name: Validate privatekey1 (test - RSA key with size 4096 bits) shell: "openssl rsa -noout -text -in {{ output_dir }}/privatekey1.pem | grep Private | sed 's/Private-Key: (\\(.*\\) bit)/\\1/'" register: privatekey1 -- name: Validate privatekey1 (assert) +- name: Validate privatekey1 (assert - RSA key with size 4096 bits) assert: that: - privatekey1.stdout == '4096' -- name: Validate privatekey2 (test) +- name: Validate privatekey2 (test - RSA key with size 2048 bits) shell: "openssl rsa -noout -text -in {{ output_dir }}/privatekey2.pem | grep Private | sed 's/Private-Key: (\\(.*\\) bit)/\\1/'" register: privatekey2 -- name: Validate privatekey2 (assert) +- name: Validate privatekey2 (assert - RSA key with size 2048 bits) assert: that: - privatekey2.stdout == '2048' -- name: Validate privatekey3 (test) +- name: Validate privatekey3 (test - DSA key with size 4096 bits) shell: "openssl dsa -noout -text -in {{ output_dir }}/privatekey3.pem | grep Private | sed 's/Private-Key: (\\(.*\\) bit)/\\1/'" register: privatekey3 -- name: Validate privatekey3 (assert) +- name: Validate privatekey3 (assert - DSA key with size 4096 bits) assert: that: - - privatekey1.stdout == '4096' + - privatekey3.stdout == '4096' + + +- name: Validate privatekey4 (test - Ensure key has been removed) + stat: + path: '{{ output_dir }}/privatekey4.pem' + register: privatekey4 + +- name: Validate privatekey4 (assert - Ensure key has been removed) + assert: + that: + - privatekey4.stat.exists == False + + +- name: Validate privatekey5 (test - Passphrase protected key + idempotence) + shell: "openssl rsa -noout -text -in {{ output_dir }}/privatekey5.pem -passin pass:ansible | grep Private | sed 's/Private-Key: (\\(.*\\) bit)/\\1/'" + register: privatekey5 + # Current version of OS/X that runs in the CI (10.11) does not have an up to date version of the OpenSSL library + # leading to this test to fail when run in the CI. However, this test has been run for 10.12 and has returned succesfully. + when: openssl_version.stdout|version_compare('0.9.8zh', '>=') + +- name: Validate privatekey5 (assert - Passphrase protected key + idempotence) + assert: + that: + - privatekey5.stdout == '4096' + when: openssl_version.stdout|version_compare('0.9.8zh', '>=') + +- name: Validate privatekey5 idempotence (assert - Passphrase protected key + idempotence) + assert: + that: + - not privatekey5_idempotence|changed + + +- name: Validate privatekey6 (test - Passphrase protected key with non ascii character) + shell: "openssl rsa -noout -text -in {{ output_dir }}/privatekey6.pem -passin pass:ànsïblé | grep Private | sed 's/Private-Key: (\\(.*\\) bit)/\\1/'" + register: privatekey6 + when: openssl_version.stdout|version_compare('0.9.8zh', '>=') + +- name: Validate privatekey6 (assert - Passphrase protected key with non ascii character) + assert: + that: + - privatekey6.stdout == '4096' + when: openssl_version.stdout|version_compare('0.9.8zh', '>=') diff --git a/test/integration/targets/openssl_publickey/tasks/main.yml b/test/integration/targets/openssl_publickey/tasks/main.yml index 8f8a7a34526..0f88d7fca43 100644 --- a/test/integration/targets/openssl_publickey/tasks/main.yml +++ b/test/integration/targets/openssl_publickey/tasks/main.yml @@ -3,11 +3,51 @@ openssl_privatekey: path: '{{ output_dir }}/privatekey.pem' - - name: Generate publickey + - name: Generate publickey - PEM format openssl_publickey: path: '{{ output_dir }}/publickey.pub' privatekey_path: '{{ output_dir }}/privatekey.pem' + - name: Generate publickey - OpenSSH format + openssl_publickey: + path: '{{ output_dir }}/publickey-ssh.pub' + privatekey_path: '{{ output_dir }}/privatekey.pem' + format: OpenSSH + # cryptography.hazmat.primitives import serialization.Encoding.OpenSSH and + # cryptography.hazmat.primitives import serialization.PublicFormat.OpenSSH constants + # appeared in version 1.4 of cryptography + when: cryptography_version.stdout|version_compare('1.4.0', '>=') + + - name: Generate publickey2 - standard + openssl_publickey: + path: '{{ output_dir }}/publickey2.pub' + privatekey_path: '{{ output_dir }}/privatekey.pem' + + - name: Delete publickey2 - standard + openssl_publickey: + state: absent + path: '{{ output_dir }}/publickey2.pub' + privatekey_path: '{{ output_dir }}/privatekey.pem' + + - name: Generate privatekey3 - with passphrase + openssl_privatekey: + path: '{{ output_dir }}/privatekey3.pem' + passphrase: ansible + cipher: aes256 + + - name: Generate publickey3 - with passphrase protected privatekey + openssl_publickey: + path: '{{ output_dir }}/publickey3.pub' + privatekey_path: '{{ output_dir }}/privatekey3.pem' + privatekey_passphrase: ansible + + - name: Generate publickey3 - with passphrase protected privatekey - idempotence + openssl_publickey: + path: '{{ output_dir }}/publickey3.pub' + privatekey_path: '{{ output_dir }}/privatekey3.pem' + privatekey_passphrase: ansible + register: publickey3_idempotence + - import_tasks: ../tests/validate.yml when: pyopenssl_version.stdout|version_compare('16.0.0', '>=') diff --git a/test/integration/targets/openssl_publickey/tests/validate.yml b/test/integration/targets/openssl_publickey/tests/validate.yml index 8bf34c6d138..bcb835c82fd 100644 --- a/test/integration/targets/openssl_publickey/tests/validate.yml +++ b/test/integration/targets/openssl_publickey/tests/validate.yml @@ -10,3 +10,52 @@ assert: that: - publickey_modulus.stdout == privatekey_modulus.stdout + +- name: Validate public key - OpenSSH format (test - privatekey's publickey) + shell: 'ssh-keygen -y -f {{ output_dir }}/privatekey.pem' + register: privatekey_publickey + when: cryptography_version.stdout|version_compare('1.4.0', '>=') + +- name: Validate public key - OpenSSH format (test - publickey) + slurp: + src: '{{ output_dir }}/publickey-ssh.pub' + register: publickey + when: cryptography_version.stdout|version_compare('1.4.0', '>=') + +- name: Validate public key - OpenSSH format (assert) + assert: + that: + - privatekey_publickey.stdout == '{{ publickey.content|b64decode }}' + when: cryptography_version.stdout|version_compare('1.4.0', '>=') + +- name: Validate publickey2 (test - Ensure key has been removed) + stat: + path: '{{ output_dir }}/publickey2.pub' + register: publickey2 + +- name: Validate publickey2 (assert - Ensure key has been removed) + assert: + that: + - publickey2.stat.exists == False + + +- name: Validate publickey3 (test - privatekey modulus) + shell: 'openssl rsa -noout -modulus -in {{ output_dir }}/privatekey3.pem -passin pass:ansible | openssl md5' + register: privatekey3_modulus + when: openssl_version.stdout|version_compare('0.9.8zh', '>=') + +- name: Validate publickey3 (test - publickey modulus) + shell: 'openssl rsa -pubin -noout -modulus < {{ output_dir }}/publickey3.pub | openssl md5' + register: publickey3_modulus + when: openssl_version.stdout|version_compare('0.9.8zh', '>=') + +- name: Validate publickey3 (assert) + assert: + that: + - publickey3_modulus.stdout == privatekey3_modulus.stdout + when: openssl_version.stdout|version_compare('0.9.8zh', '>=') + +- name: Validate publickey3 idempotence (assert) + assert: + that: + - not publickey3_idempotence|changed diff --git a/test/integration/targets/setup_openssl/tasks/main.yml b/test/integration/targets/setup_openssl/tasks/main.yml index 282ee43dd49..a49714cfdeb 100644 --- a/test/integration/targets/setup_openssl/tasks/main.yml +++ b/test/integration/targets/setup_openssl/tasks/main.yml @@ -20,6 +20,14 @@ name: pyOpenSSL when: ansible_os_family == 'Darwin' -- name: register openssl version +- name: register pyOpenSSL version command: python -c 'import OpenSSL; print(OpenSSL.__version__)' register: pyopenssl_version + +- name: register openssl version + shell: "openssl version | cut -d' ' -f2" + register: openssl_version + +- name: register cryptography version + command: python -c 'import cryptography; print(cryptography.__version__)' + register: cryptography_version