2023年1月14日土曜日

AnsibleでVyOSを操作する

自宅ではAnsibleを導入してから、以下OSやソフトウェアに対して作業のコード化や自動化を行ってきた。導入手順などは以下記事を参照。

OS/ソフトウェア URL
Linux AnsibleでLinuxサーバの初期設定と単体テストを行う
Windows Server AnsibleからWindows Serverに接続するための事前作業
vSphere ESXi AnsibleでESXi上に仮想マシンを構築する

今回は、Ansibleを使って仮想ルーターOSであるVyOSを操作する手順を記載する。VyOSは、Ansibleに標準で導入されているvyos.vyosというモジュールで操作可能となる。

環境

今回手順を確認した環境は以下の通り。

  • コントロールノード
    • OS : AlmaLinux 8.5
    • Ansible : ansible [core 2.13.1]
  • VyOS : 1.4

VyOSの各種設定を行うPlaybook

今回のPlaybookでは、VyOSに対して各種設定を行い、設定後にコンフィグをファイルとして保存する。

以下にPlaybookの内容を説明する。

Playbook説明

変数 (vars)

Ansibleを使ってVyOSを操作する際の変数は、以下の通り設定する。VyOSはCisco機器のようなansible_become_methodの設定値は不要となる。

変数 設定値
ansible_user 任意のSSH接続用ユーザを設定。今回はvyosで設定。
ansible_password 任意のSSH接続用ユーザのパスワードを設定。
ansible_connection network_cli
ansible_network_os vyos
vyos_interface インタフェースのdescriptionやIPアドレスを設定。
vyos_ntp_server 時刻同期先のNTPサーバを設定。
vyos_snmp SNMPに必要となるコミュニティ名やTrapターゲットを設定する。なお、TrapソースのIPアドレス指定する際に、vyos_interfaceの変数からIPアドレスを取得している。
vyos_route スタティックルートを設定。

タスク (tasks)

Playbookのタスクは以下の通り。主にvyos.vyosモジュールで個別に用意されている設定モジュールを使って設定可能なものをタスクとして作成した。これ以外の設定に関しては、vyos.vyos.vyos_configを使って、VyOSのコマンドを直接流して設定することができる(当然その際には冪等性には注意すること)。

タスク モジュール 説明
事前config保存(save) & configファイル取得 vyos.vyos.vyos_config VyOSのconfigを取得する。取得内容はshow configuration commandsの内容となる。また、configが保存されていないことを想定して、save: trueを設定する。
タイムゾーン設定 vyos.vyos.vyos_config タイムゾーンを設定する。タイムゾーンを設定するモジュールはないため、直接設定コマンドを実行する。
ホスト名設定 vyos.vyos.vyos_hostname ホスト名を設定する。
インタフェース設定 vyos.vyos.vyos_interfaces インタフェースのdescriptionを設定する。本来はIPアドレスもこのモジュールで設定できるが、次のタスクでIPv6を無効化する際に、IPアドレスの再設定が必要になることから、本タスクでは設定は省略している。
インタフェース設定 (IPアドレス設定 & IPv6 無効化) vyos.vyos.vyos_config IPアドレスとIPv6の無効化設定を直接コマンドにて設定する。
SSH & Console設定 vyos.vyos.vyos_config SSHとConsoleの設定を直接設定コマンドにて設定する
NTP設定 vyos.vyos.vyos_ntp_global NTPによる時刻同期先の設定を行う。
SNMP設定 vyos.vyos.vyos_snmp_server SNMPの設定を行う。SNMP Trapのターゲット設定は本モジュールでは動作に失敗したため、次のタスクにて直接コマンドにて設定する。
SNMP設定 (Trap target) vyos.vyos.vyos_config SNMP Trapのターゲット設定を直接コマンドにて設定する。
スタティックルート設定 vyos.vyos.vyos_static_routes スタティックルートを設定する。
事後config保存(save) & configファイル取得 vyos.vyos.vyos_config VyOSのconfigを取得する。
ファイル差分確認 ansible.builtin.command Linuxのdiffコマンドにて、設定前後のconfig比較を行う。
ファイル差分表示 ansible.builtin.debug diffコマンドの比較結果を表示する。

Playbook

以上をふまえ、Playbook全体は以下となる。

set_vyos_global_settings.yml

---
- name: Set vyos global settings
  gather_facts: true
  hosts: vyos

  vars:
    ansible_user: vyos
    ansible_password: 'P@ssw0rd!'
    ansible_connection: network_cli
    ansible_network_os: vyos

    vyos_interface:
      - name: eth0
        description: OUTSIDE
        address: 192.168.3.4
        prefix: 24
      - name: eth1
        description: INSIDE
        address: 192.168.1.4
        prefix: 24

    vyos_ntp_server:
      - server: 192.168.3.2

    vyos_snmp:
      community: public
      authorization_type: ro
      trap_source: '{{ (vyos_interface | selectattr("name", "==", "eth1") | first).address }}'
      trap_target: 192.168.1.24
      trap_community: public

    vyos_route:
      dest: 0.0.0.0/0
      next_hop: 192.168.3.254

  tasks:
    # 事前config保存(save) & configファイル取得
    - name: Get configuration command
      vyos.vyos.vyos_config:
        backup: true
        backup_options:
          dir_path: "{{ lookup('env', 'PWD') }}/vyos"
          filename: "{{ inventory_hostname }}_conf_before.log"
        save: true

    # タイムゾーン設定
    - name: Set timezone
      vyos.vyos.vyos_config:
        lines:
          - set system time-zone 'Asia/Tokyo'

    # ホスト名設定
    - name: Set hostname
      vyos.vyos.vyos_hostname:
        config:
          hostname: "{{ inventory_hostname }}"
        state: replaced

    # インタフェース設定
    - name: Set interfaces
      vyos.vyos.vyos_interfaces:
        config:
          - name: "{{ item.name }}"
            description: "{{ item.description }}"
        state: replaced
      loop: "{{ vyos_interface }}"

    # インタフェース設定 (IPアドレス設定 & IPv6 無効化)
    - name: Set interfaces ipv6 address
      vyos.vyos.vyos_config:
        lines:
          - set interfaces ethernet {{ item.name }} address {{ item.address }}/{{ item.prefix }}
          - set interfaces ethernet {{ item.name }} ipv6 address no-default-link-local
      loop: "{{ vyos_interface }}"

    # SSH & Console設定
    - name: Set SSH and consle
      vyos.vyos.vyos_config:
        lines:
          - set service ssh listen-address '{{ (vyos_interface | selectattr("name", "==", "eth1") | first).address }}'
          - delete system console

    # NTP設定
    - name: Set ntp server
      vyos.vyos.vyos_ntp_global:
        config:
          servers: "{{ vyos_ntp_server }}"
        state: replaced

    # SNMP設定
    # Trap targetは「requires a community to be set!」のエラーで失敗する
    # 設定をすべて置き換えをしないよう、state: mergedを指定
    - name: Set snmp
      vyos.vyos.vyos_snmp_server:
        config:
          communities:
            - name: "{{ vyos_snmp.community }}"
              authorization_type: "{{ vyos_snmp.authorization_type }}"
          trap_source: "{{ vyos_snmp.trap_source }}"
        state: merged

    # SNMP設定 (Trap target)
    - name: Set snmp trap target
      vyos.vyos.vyos_config:
        lines:
          - set service snmp trap-target {{ vyos_snmp.trap_target }} community {{ vyos_snmp.trap_community }}

    # スタティックルート設定
    - name: Set static route
      vyos.vyos.vyos_static_routes:
        config:
          - address_families:
              - afi: ipv4
                routes:
                  - dest: "{{ vyos_route.dest }}"
                    next_hops:
                      - forward_router_address: "{{ vyos_route.next_hop }}"
        state: replaced

    # 事後config保存(save) & configファイル取得
    - name: Get configuration command
      vyos.vyos.vyos_config:
        backup: true
        backup_options:
          dir_path: "{{ lookup('env', 'PWD') }}/vyos"
          filename: "{{ inventory_hostname }}_conf_after.log"
        save: true

    # ファイル差分確認
    # 差分があった場合も処理を継続するため、failed_when: falseを付与
    - name: Diff configuration
      ansible.builtin.command: |
        diff "vyos/{{ inventory_hostname }}_conf_before.log" "vyos/{{ inventory_hostname }}_conf_after.log"
      register: result
      changed_when: false
      delegate_to: localhost
      failed_when: false

    # ファイル差分表示
    - name: Show configuration differences
      ansible.builtin.debug:
        msg: "{{ result.stdout_lines }}"

実行結果

Playbookの実行結果は以下の通り。事前と事後でconfig比較を行い、設定差分がわかるようになっている。

# ansible-playbook -i hosts -l t3034vyos vyos/set_vyos_global_settings_sample.yml 

PLAY [Set vyos global settings] ********************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************************
ok: [t3034vyos]

TASK [Get configuration command] *******************************************************************************************************************************************
changed: [t3034vyos]

TASK [Set timezone] ********************************************************************************************************************************************************
[WARNING]: To ensure idempotency and correct diff the input configuration lines should be similar to how they appear if present in the running configuration on device
changed: [t3034vyos]

TASK [Set hostname] ********************************************************************************************************************************************************
changed: [t3034vyos]

TASK [Set interfaces] ******************************************************************************************************************************************************
changed: [t3034vyos] => (item={'name': 'eth0', 'description': 'OUTSIDE', 'address': '192.168.3.34', 'prefix': 24})
changed: [t3034vyos] => (item={'name': 'eth1', 'description': 'INSIDE', 'address': '192.168.1.34', 'prefix': 24})

TASK [Set interfaces ipv6 address] *****************************************************************************************************************************************
changed: [t3034vyos] => (item={'name': 'eth0', 'description': 'OUTSIDE', 'address': '192.168.3.34', 'prefix': 24})
changed: [t3034vyos] => (item={'name': 'eth1', 'description': 'INSIDE', 'address': '192.168.1.34', 'prefix': 24})

TASK [Set SSH and consle] **************************************************************************************************************************************************
changed: [t3034vyos]

TASK [Set ntp server] ******************************************************************************************************************************************************
changed: [t3034vyos]

TASK [Set snmp] ************************************************************************************************************************************************************
changed: [t3034vyos]

TASK [Set snmp trap target] ************************************************************************************************************************************************
changed: [t3034vyos]

TASK [Set static route] ****************************************************************************************************************************************************
changed: [t3034vyos]

TASK [Get configuration command] *******************************************************************************************************************************************
changed: [t3034vyos]

TASK [Diff configuration] **************************************************************************************************************************************************
ok: [t3034vyos -> localhost]

TASK [Show configuration differences] **************************************************************************************************************************************
ok: [t3034vyos] => {
    "msg": [
        "0a1,2",
        "> set interfaces ethernet eth0 address '192.168.3.4/24'",
        "> set interfaces ethernet eth0 description 'OUTSIDE'",
        "1a4",
        "> set interfaces ethernet eth0 ipv6 address no-default-link-local",
        "3a7",
        "> set interfaces ethernet eth1 description 'INSIDE'",
        "4a9",
        "> set interfaces ethernet eth1 ipv6 address no-default-link-local",
        "6c11,15",
        "< set service ssh",
        "---",
        "> set protocols static route 0.0.0.0/0 next-hop 192.168.3.254",
        "> set service snmp community public authorization 'ro'",
        "> set service snmp trap-source '192.168.1.4'",
        "> set service snmp trap-target 192.168.1.24 community 'public'",
        "> set service ssh listen-address '192.168.1.4'",
        "15,16c24",
        "< set system console device ttyS0 speed '115200'",
        "< set system host-name 'vyos'",
        "---",
        "> set system host-name 't3034vyos'",
        "18,20c26",
        "< set system ntp server time1.vyos.net",
        "< set system ntp server time2.vyos.net",
        "< set system ntp server time3.vyos.net",
        "---",
        "> set system ntp server 192.168.3.2",
        "22c28,29",
        "< set system syslog global facility protocols level 'debug'",
        "\\ ファイル末尾に改行がありません",
        "---",
        "> set system syslog global facility protocols level 'debug'",
        "> set system time-zone 'Asia/Tokyo'",
        "\\ ファイル末尾に改行がありません"
    ]
}

PLAY RECAP *****************************************************************************************************************************************************************
t3034vyos                  : ok=14   changed=11   unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

VyOSのconfigファイルも以下の通り出力される。

vyos/t3034vyos_conf_after.log

set interfaces ethernet eth0 address '192.168.3.4/24'
set interfaces ethernet eth0 description 'OUTSIDE'
set interfaces ethernet eth0 hw-id '00:0c:29:b8:da:49'
set interfaces ethernet eth0 ipv6 address no-default-link-local

~(以下略)~

以上で、Ansibleを使って仮想ルーターOSであるVyOSを操作する手順は完了となる。

0 件のコメント:

コメントを投稿

人気の投稿