Ansible Vault 機能のソースコードを読んでみた
Posted on
Ansible 1.5 から DB のパスワードや API の認証情報といった機密情報を暗号化する Vault 機能が提供されています。
あらかじめ設定したパスワードを使って機密ファイルを共通鍵認証で暗号化する機能ですが、そもそも Vault 機能を信じても大丈夫なのでしょうか? 機密情報を任せるのであれば、どういった暗号アルゴリズムで処理されているのかちゃんと理解しておきたいものです。
というわけで、ansible-vault
コマンドの実装を読んでみました。今回読んだのは 5/25 にリリースされた v2.1.0.0-1 です。 GitHub 上のソースコードへリンクを貼っているので、詳しく読みたい方は参考にしてください。
Ansible のソースコードを読み解く
ansible-vault
コマンドの実体は lib/ansible/cli/vault.py
です。
これは CLI のインターフェイスのみで、実際の処理は import された ansible.parsing.vault
のパッケージで行われています。
暗号化や復号化といった処理はすべて lib/ansible/parsing/vault/__init__.py
に集約されています。処理を追いかけると ValutLib
クラスの encrypt
関数で暗号アルゴリズムが選択されることがわかります。対応している暗号アルゴリズムは AES
と AES256
で、デフォルトは AES256
が使われます。
class VaultLib:
def encrypt(self, data):
(snip)
if not self.cipher_name or self.cipher_name not in CIPHER_WRITE_WHITELIST:
self.cipher_name = u"AES256"
try:
Cipher = CIPHER_MAPPING[self.cipher_name]
except KeyError:
raise AnsibleError(u"{0} cipher could not be found".format(self.cipher_name))
this_cipher = Cipher()
# encrypt data
b_enc_data = this_cipher.encrypt(b_data, self.b_password)
最後の this_cipher.encrypt
で VaultAES256
クラスの encrypt
関数が実行されます。
class VaultAES256:
def encrypt(self, data, password):
salt = os.urandom(32)
key1, key2, iv = self.gen_key_initctr(password, salt)
# PKCS#7 PAD DATA http://tools.ietf.org/html/rfc5652#section-6.3
bs = AES.block_size
padding_length = (bs - len(data) % bs) or bs
data += to_bytes(padding_length * chr(padding_length), encoding='ascii', errors='strict')
# COUNTER.new PARAMETERS
# 1) nbits (integer) - Length of the counter, in bits.
# 2) initial_value (integer) - initial value of the counter. "iv" from gen_key_initctr
ctr = Counter.new(128, initial_value=int(iv, 16))
# AES.new PARAMETERS
# 1) AES key, must be either 16, 24, or 32 bytes long -- "key" from gen_key_initctr
# 2) MODE_CTR, is the recommended mode
# 3) counter=<CounterObject>
cipher = AES.new(key1, AES.MODE_CTR, counter=ctr)
# ENCRYPT PADDED DATA
cryptedData = cipher.encrypt(data)
ここが今回の肝になるソースコードです。大まかな流れとしては、
- 渡ってきたパスワードと urandom で生成したソルトから
key1
,key2
,iv
を生成 - ブロック長に足りない部分を padding 処理
Counter.new
で AES-CTR モードで必要なカウンターブロックを計算AES.new
して AES-CTR モードで暗号化
AES
は Crypto.Cipher
から import されたものです。
# AES IMPORTS
try:
from Crypto.Cipher import AES as AES
HAS_AES = True
except ImportError:
HAS_AES = False
Crypto.Cipher
は何だろうと調べてみたら PyCrypto というパッケージでした。
というわけで、Vault 機能は独自方式で暗号化されているわけではなく、PyCrypto パッケージを使って AES256 で暗号化されている ことがわかりました。
まとめ
今回ソースコードを読んでみて Vault 機能は信用しても大丈夫だと自信を持って言えるようになりました。 AES の CTR モードのように新しい知見も得られたので、ソースコードを読んでみるのは大切ですね。