はじめに
先日、Ansible 4系から 6系にバージョンアップしたら、今まで正常に動いていた Cisco IOS に対して処理する Playbook が動かなくなる現象に出会いました。
以下のように、認証エラーです。
fatal: [sw01]: FAILED! => {"changed": false, "msg": "Failed to authenticate: Authentication failed."}
認証情報は変えてないので不思議でした。
Ansible 4系は ansible-core としては 2.11 系、Ansible 6系は 2.13 系です。この間で仕様変更的になことが起きたようです。
調べてみると、どうやら、変数として ansible_password
を設定していても、デフォルトの秘密鍵 ~/.ssh/id_rsa
を見つけてそれを利用しているためのようでした(ペアの公開鍵は対象に未設定)。
以下の条件で再現しました。
ansible.netcommon
コレクション 2.4.0 以上- ansible-core 2.12.9 以上 or ansible-core 2.13.4 以上
- コントロールノードに
~/.ssh/id_rsa
あり。ただし、ペアの公開鍵はターゲットノードに仕込んでいない - paramiko 使用
以下、調べたことなどをまとめます。
基本前提環境
再現
再現を試みた環境やファイルについてです。
環境
~/.ssh/id_rsa
がある状態です。
ただし、この秘密鍵のペアの公開鍵は、Ansible の接続対象の機器には仕込んでいない状況です。
ファイル類
イベントリファイル
インベントリファイルは以下のようなものを利用しました。
inventory.ini
[ios] ios01 ansible_host=192.168.1.11
変数定義ファイル
インベントリファイルで定義した ios
グループの変数は以下の通りです。
認証には公開鍵認証方式ではなく、パスワード認証(ansible_password
変数)を利用する意図です。
group_vars/ios.yml
--- ansible_network_os: cisco.ios.ios ansible_connection: ansible.netcommon.network_cli ansible_user: dummy_user ansible_password: dummy_password # パスワード認証を利用したい
Playbook
実行する Playbook は、show コマンドを実行するだけの簡単なものです。
ios_show.yml
--- - hosts: ios01 gather_facts: false tasks: - name: show version cisco.ios.ios_command: commands: - show version
実行
ansible-core 2.11 系のころは正常に実行できましたが、今回は
の環境で実行します。
$ ansible-playbook -i inventory.ini ios_show.yml PLAY [ios01] ************************************************************************************* TASK [show version] ****************************************************************************** [WARNING]: ansible-pylibssh not installed, falling back to paramiko fatal: [ios01]: FAILED! => {"changed": false, "msg": "Failed to authenticate: Authentication failed."} PLAY RECAP *************************************************************************************** ios01 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
認証エラー Failed to authenticate: Authentication failed.
となってしまいました。
なお、~/.ssh/id_rsa
を適当な名前に変更したら OK になりました。
NG条件まとめ
色々試していくと、冒頭にも書いた通り、以下の条件でNGとなりました。
ansible.netcommon
コレクション 2.4.0 以上- ansible-core 2.12.9 以上 or ansible-core 2.13.4 以上
- コントロールノードに
~/.ssh/id_rsa
あり。ただし、ペアの公開鍵はターゲットノードに仕込んでいない - paramiko 使用
対処
ansible-core や ansible.netcommon コレクションのバージョンはそのまま、かつパスワード認証のままにしても、対処方法はいくつか考えらます。
対処1: デフォルトの秘密鍵を探さないように指定
今回は、デフォルトの秘密鍵(今回は ~/.ssh/id_rsa
)を探して見つかって、それを使ったがターゲットノードにペアの公開鍵がないくて認証エラーという状態でした。
パスワード認証を利用したい場合は、秘密鍵を探さないように指定できます。PARAMIKO_LOOK_FOR_KEYS
という設定項目を利用します。
ansible.cfg
で指定する場合
[paramiko_connection] look_for_keys = False
環境変数で指定する場合
ANSIBLE_PARAMIKO_LOOK_FOR_KEYS=False
対処2: pylibssh
を利用する
ネットワークモジュールが長らく内部的に利用している SSH クライアントライブラリとして、paramiko
があります。
最近は pylibssh
(Pythonのパッケージ名としてはansible-pylibssh
)というもの新しいものもあります。登場した経緯は Ansible の公式ブログの記事に書かれています。
今回は、paramiko
から pylibssh
を利用するように変更したら、パスワード認証のまま認証が通るようになりました。
手順は以下の通りです。
pylibssh のインストール
pip でインストールする。
pip install ansible-pylibssh
pylibssh を利用するように指定
group_vars/ios.yml
に以下を追加する。yaml ansible_network_cli_ssh_type: libssh
なお、ansible.netcommon
コレクション3.0.0
以上では、デフォルトのansible_network_cli_ssh_type: auto
の場合でもpylibssh
がインストールされていればpylibssh
を優先で使う仕様になりました。そのため、厳密にはansible.netcommon
コレクション3.0.0
以上では、明示的な指定は必要ありません。他にも、環境変数で指定する方法もあります。詳細は
ansible.netcommon.network_cli
コネクションプラグインのドキュメント のssh_type
パラメーターの欄を参照してください。
環境によっては、pylibssh を利用するようにしたことで別の現象が発生することもあるので、これはこれで確認が必要そうです。
参考
- ansible-core 2.12.9 の changelog
Fix for network_cli not getting all relevant connection options
- 関連 PR https://github.com/ansible/ansible/pull/74446
- ansible-core 2.13.4 の changelog
Fix for network_cli not getting all relevant connection options
- 関連 PR https://github.com/ansible/ansible/pull/78591
ansble.netcommon
4.0.0 時点の実装として、password
の指定があって private_key_file
の指定がない場合は鍵を探さない、という実装は残っていそうだが、詳細は追えていない。これに頼らないほうが無難そう。
おまけ: 試行錯誤ツイート
ansible 6.4.0 (ansible-core は 2.13.5) と paramiko の組わせで ios 機器に接続したときに Failed to authenticate: Authentication failed. になってしまう現象に遭遇。
— よこち (@akira6592) 2022年10月27日
ansible 4 (ansible-core 2.11) だ大丈夫。