てくなべ (tekunabe)

ansible / network / automation / StackStorm

【Ansible】ios_config モジュールで特権ユーザーにもかかわらず operation requires privilege escalation が発生する不具合について(2.7.1で修正)

■ 現象: 特権ユーザーにもかかわらずエラーに

特定条件下で、Ansible の ios_config モジュール使用時に、特権ユーザーを利用しているにもかかわらず、operation requires privilege escalation というエラーが表示され、Playbook の処理が中断されてしまう不具合があります。

github.com

通常であれば、operation requires privilege escalation というエラーは、コンフィグ投入などの特権が必要な操作を、特権でない状態で実行しようとしたときに表示されるエラーです。この不具合では、特権ユーザーであってもこのエラーが表示されてしまいます。

この不具合は数ヶ月見守っていたのですが、ここ最近で修正の動きがあったため、記事にまとめました。


■ 発生条件

私が試したり、調べたりした範囲では、以下のような発生条件となりました。

  • コマンド実行結果の取得に時間がかかり、コマンド実行結果に > が含まれる

検証環境は Ansible 2.7.0 です。 どうやら、処理に時間がかかるとプロンプト以外に > が含まれている場合、それをユーザー(非特権)EXEC モードのプロンプトの一部として解釈してしまい「権限がないのでこのコマンドは実行できない」という判断になってしまうようです。

  • (参考)プロンプトの例
iso1>          # ユーザーEXECモードの例
ios1> enable
ios1#          # 特権EXECモードの例

プロンプト以外に > が含まれる可能性があるのは、インターフェースの description や、SNMP コミュニティ名、ACL名などです。

対象バージョンの範囲は、こちらのコメント

The root cause of the issue is in the fix made in PR #39063 (from 2.5.2 onwards) to handle the ios device on which \r\n characters are not prefixed before prompt

を含めて考えると以下のようになると思います。 (2.7.0以外未検証)

  • 2.5 系: 2.5.2 以降
  • 2.6 系: 2.6.0 以降
  • 2.7 系: 2.7.0 (現状本バージョンのみ)


■ 対策見込み

修正分が Ansible 2.7.1 としてリリースされる予定です。 このリリースでは、 network_cli コネクションプラグインのパラメーターとして、 persistent_buffer_read_timeout が追加される予定です。このパラメーターで、プロンプトを解釈するタイミングを調整して対策することになります。デフォルトは 0.1 秒です。

なお現状、2.7.1はリリースされていませんが、

pip install git+https://github.com/ansible/ansible.git@stable-2.7

でインストールできるバージョン「ansible 2.7.0.post0」で確認したところ、エラーは出なくなりました。 場合に読もると思いますが、persistent_buffer_read_timeout は調整不要でした。

[2018/10/26 追記] Ansible 2.7.1 がリリースされました。 https://github.com/ansible/ansible/blob/stable-2.7/changelogs/CHANGELOG-v2.7.rst#v2-7-1

Fix prompt mismatch issue for ios (https://github.com/ansible/ansible/issues/47004)


■(参考)検証内容詳細

私が検証した方法を記載します。

コンフィグテンプレートの準備

現象を再現するための仕込みのコンフィグとして、以下を準備します。

{% for i in range(200) %}
snmp-server community com{{ i }} RO h>oge
{% endfor %}

コマンドの最後のオプション(ACL名)であるh>oge> を含めているのがポイントです。

この Jinja2 テンプレートは、以下のようなコマンドを生成します。

snmp-server community com0 RO h>oge
snmp-server community com1 RO h>oge
snmp-server community com2 RO h>oge
...(略)...
snmp-server community com198 RO h>oge
snmp-server community com199 RO h>oge

Playbook の準備

- hosts: iosprg
  gather_facts: no

  tasks:
    - name: set snmp
     ios_config:
        src: snmp.j2

  vars:
    ansible_command_timeout: 300  # 投入に時間がかかるためデフォルト 10 秒より伸ばす

コンフィグの仕込み

準備したコンフィグテンプレートをいったん流し込みます。

$ ansible-playbook -i inventory.ini insert.yml

これで準備が整いました。

再現試行

対象機器が、

  • コマンド実行結果の取得に時間がかかり、コマンド実行結果に > が含まれる

という状態になったところで、もう一度同じ Playbook を実行します。通常であれば、変更なしで正常終了するはずです。

$ ansible-playbook -i inventory.ini set_snmp.yml

PLAY [iosprg] *******************************************************************************************************************

TASK [set snmp] ***************************************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible.module_utils.connection.ConnectionError: operation requires privilege escalation
fatal: [iosprg1]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/home/vagrant/.ansible/tmp/ansible-local-6780JjM2oM/ansible-tmp-1540298326.36-234692567510359/AnsiballZ_ios_config.py\", line 113, in <module>\n _ansiballz_main()\n  File \"/home/vagrant/.ansible/tmp/ansible-local-6780JjM2oM/ansible-tmp-1540298326.36-234692567510359/AnsiballZ_ios_config.py\", line 105, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/home/vagrant/.ansible/tmp/ansible-local-6780JjM2oM/ansible-tmp-1540298326.36-234692567510359/AnsiballZ_ios_config.py\", line 48, in invoke_module\n    imp.load_module('__main__', mod, module, MOD_DESC)\n  File \"/tmp/ansible_ios_config_payload_4kli5Q/__main__.py\",line 536, in <module>\n  File \"/tmp/ansible_ios_config_payload_4kli5Q/__main__.py\", line 467, in main\n  File \"/tmp/ansible_ios_config_payload_4kli5Q/__main__.py\", line 328, in edit_config_or_macro\n  File \"/tmp/ansible_ios_config_payload_4kli5Q/ansible_ios_config_payload.zip/ansible/module_utils/connection.py\", line 173, in __rpc__\nansible.module_utils.connection.ConnectionError: operation requires privilege escalation\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
        to retry, use: --limit @/vagrant/demo/insert.retry

PLAY RECAP **********************************************************************************************************************
iosprg1                    : ok=0    changed=0    unreachable=0    failed=1

operation requires privilege escalation というエラーが発生しました。 ios_config モジュールは、指定されたコンフィグ投入前の事前コンフィグを取得処理があるため、その際に > が引っかかってしまったのではないかと思います。

なお、ACL名のところを変えていくつかのパターンで試したところ、以下のような結果になりました。

  • エラー発生
    • h>oge
    • hoge>
  • 正常
    • hoge


■ 関連 issue / PR