From 58cccce3843031dc073533e0028ca929ae047817 Mon Sep 17 00:00:00 2001 From: Vilmos Nebehaj Date: Wed, 22 Jul 2015 19:52:42 +0200 Subject: [PATCH 1/2] Use PBKDF2HMAC() from cryptography for vault keys. When stretching the key for vault files, use PBKDF2HMAC() from the cryptography package instead of pycrypto. This will speed up the opening of vault files by ~10x. The problem is here in lib/ansible/utils/vault.py: hash_function = SHA256 # make two keys and one iv pbkdf2_prf = lambda p, s: HMAC.new(p, s, hash_function).digest() derivedkey = PBKDF2(password, salt, dkLen=(2 * keylength) + ivlength, count=10000, prf=pbkdf2_prf) `PBKDF2()` calls a Python callback function (`pbkdf2_pr()`) 10000 times. If one has several vault files, this will cause excessive start times with `ansible` or `ansible-playbook` (we experience ~15 second startup times). Testing the original implementation in 1.9.2 with a vault file: In [2]: %timeit v.decrypt(encrypted_data) 1 loops, best of 3: 265 ms per loop Having a recent OpenSSL version and using the vault.py changes in this commit: In [2]: %timeit v.decrypt(encrypted_data) 10 loops, best of 3: 23.2 ms per loop --- lib/ansible/parsing/vault/__init__.py | 42 ++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/lib/ansible/parsing/vault/__init__.py b/lib/ansible/parsing/vault/__init__.py index f3cee27ea47..6df786a212f 100644 --- a/lib/ansible/parsing/vault/__init__.py +++ b/lib/ansible/parsing/vault/__init__.py @@ -81,6 +81,18 @@ try: except ImportError: HAS_AES = False +# OpenSSL pbkdf2_hmac +HAS_PBKDF2HMAC = False +try: + from cryptography.hazmat.primitives.hashes import SHA256 as c_SHA256 + from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC + from cryptography.hazmat.backends import default_backend + HAS_PBKDF2HMAC = True +except ImportError: + pass + +HAS_ANY_PBKDF2HMAC = HAS_PBKDF2 or HAS_PBKDF2HMAC + CRYPTO_UPGRADE = "ansible-vault requires a newer version of pycrypto than the one installed on your platform. You may fix this with OS-specific commands such as: yum install python-devel; rpm -e --nodeps python-crypto; pip install pycrypto" HEADER=u'$ANSIBLE_VAULT' @@ -89,7 +101,7 @@ CIPHER_WHITELIST=['AES', 'AES256'] def check_prereqs(): - if not HAS_AES or not HAS_COUNTER or not HAS_PBKDF2 or not HAS_HASH: + if not HAS_AES or not HAS_COUNTER or not HAS_ANY_PBKDF2HMAC or not HAS_HASH: raise AnsibleError(CRYPTO_UPGRADE) class VaultLib(object): @@ -551,13 +563,7 @@ class VaultAES256(object): check_prereqs() - def gen_key_initctr(self, password, salt): - # 16 for AES 128, 32 for AES256 - keylength = 32 - - # match the size used for counter.new to avoid extra work - ivlength = 16 - + def create_key(self, password, salt, keylength, ivlength): hash_function = SHA256 # make two keys and one iv @@ -566,6 +572,26 @@ class VaultAES256(object): derivedkey = PBKDF2(password, salt, dkLen=(2 * keylength) + ivlength, count=10000, prf=pbkdf2_prf) + return derivedkey + + def gen_key_initctr(self, password, salt): + # 16 for AES 128, 32 for AES256 + keylength = 32 + + # match the size used for counter.new to avoid extra work + ivlength = 16 + + if HAS_PBKDF2HMAC: + backend = default_backend() + kdf = PBKDF2HMAC( + algorithm=c_SHA256(), + length=2 * keylength + ivlength, + salt=salt, + iterations=10000, + backend=backend) + derivedkey = kdf.derive(password) + else: + derivedkey = self.create_key(password, salt, keylength, ivlength) key1 = derivedkey[:keylength] key2 = derivedkey[keylength:(keylength * 2)] From 956fa801157cc622083d7155316c4db333e1a02a Mon Sep 17 00:00:00 2001 From: Vilmos Nebehaj Date: Tue, 28 Jul 2015 21:17:51 +0200 Subject: [PATCH 2/2] Add note about installing cryptography. --- docsite/rst/playbooks_vault.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docsite/rst/playbooks_vault.rst b/docsite/rst/playbooks_vault.rst index 5cb1eb90c9c..cd294788b8b 100644 --- a/docsite/rst/playbooks_vault.rst +++ b/docsite/rst/playbooks_vault.rst @@ -108,5 +108,11 @@ This is something you may wish to do if using Ansible from a continuous integrat (The `--vault-password-file` option can also be used with the :ref:`ansible-pull` command if you wish, though this would require distributing the keys to your nodes, so understand the implications -- vault is more intended for push mode). +.. _speeding_up_vault: +Speeding Up Vault Operations +```````````````````````````` +By default, Ansible uses PyCrypto to encrypt and decrypt vault files. If you have many encrypted files, decrypting them at startup may cause a perceptible delay. To speed this up, install the cryptography package:: + + pip install cryptography