自動化ツールの一つである「Ansible」について、これから学ぼうという方、使っていきたい方を対象に、導入方法から実用例までを簡単に紹介していきます。第3回は複数のシステムに対するバージョンチェックや脆弱性対策を一度に実行する方法を解説します。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
システム管理者が常に頭を悩ませる問題として、セキュリティ対策は外せないでしょう。日々発表される脆弱(ぜいじゃく)性情報の中に、ゼロデイ脆弱性が存在すると速やかな対応を進める必要がでてきます。こうした脆弱性に対してシステム管理者の皆さまは常にセキュリティ対策が実施できるよう備えているでしょう。
しかし、いざ対策を実施するにしても、現在管理しているシステムが脆弱性の影響を受けるバージョンかどうかを確認するために、利用中のバージョンを確認する作業が伴います。確認する対象のシステム数が多ければ多いほど確認にかける時間は膨大なものとなりますし、対策を実施する時間も必要です。
今回は、このような場面においてAnsibleを利用し、複数のシステムに対するバージョンチェックや脆弱性対策を一度に実行する方法を紹介します。
脆弱性対応を始めるに当たり、まずは管理対象のシステムにインストールされているソフトウェアのバージョン情報を収集する必要があります。基本的には、システムの設計書などに導入しているソフトウェアやパッケージのバージョンが定義されているものですが、突発的な対応などで本番環境には違うバージョンが導入されていることもあるでしょう。
そのために、システムごとにバージョン情報を収集する必要がありますが、一つ一つの環境にログインして確認するのは手間がかかります。シェルスクリプトを作成するのもよいですが、一から実装すると大変な上、内容を変更したい場合は手作業が必要になります。そこでAnsibleを使ってバージョン情報を簡単に収集してみましょう。
Ansibleでシステムのパッケージバージョン情報を取得するモジュールはansible.builtin.package_factsです。このモジュールを実行すると、パッケージ名をキーとする辞書型変数であるansible_facts.packagesにシステムのパッケージ情報が登録されます。
まずは動作を確認するために、以下のようなPlaybookを作成します。ファイル名はpackage.ymlとしています。ここでは、ansible.builtin.debugモジュールを使ってansible.builtin.package_factsモジュールで取得したパッケージ情報のうち、nginx パッケージの情報のみ表示しています。
- hosts: webserver tasks: - name: パッケージ情報を収集する ansible.builtin.package_facts: - name: nginx パッケージのバージョン情報を表示する ansible.builtin.debug: var: ansible_facts.packages['nginx']
作成したPlaybookを以下のように実行します。実行の際に必要なインベントリファイルのhostsは第2回の記事と同一のものになりますので割愛します。
$ ansible-playbook -i hosts package.yml PLAY [webserver] **************************************************************************************************** TASK [Gathering Facts] ********************************************************************************************** ok: [ubuntu2204] ok: [rocky9] TASK [パッケージ情報を収集する] ************************************************************************************* ok: [rocky9] ok: [ubuntu2204] TASK [nginx パッケージのバージョン情報を表示する] ******************************************************************* ok: [rocky9] => { "ansible_facts.packages['nginx']": [ { "arch": "x86_64", "epoch": 1, "name": "nginx", "release": "10.el9", "source": "rpm", "version": "1.20.1" } ] } ok: [ubuntu2204] => { "ansible_facts.packages['nginx']": [ { "arch": "amd64", "category": "web", "name": "nginx", "origin": "Ubuntu", "source": "apt", "version": "1.18.0-6ubuntu14.3" } ] } PLAY RECAP ********************************************************************************************************** rocky9 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 ubuntu2204 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
実行した結果を確認すると、ansible_facts.packages['nginx']にはパッケージ情報がリスト型で登録されていることが分かります。これは、kernelパッケージのように同一パッケージ名で複数のバージョンがインストールされていることもあるため、このような仕様になっています。またOSごとに取得できる情報に差があることも分かります。Rocky Linux ではバージョン情報としてversionの他にreleaseも併せて確認する必要がありますが、Ubuntuではversionだけでバージョン情報が確認できます。
これでAnsibleにおけるパッケージのバージョン情報を取得する際の挙動が分かりました。これを基に、環境ごとのパッケージバージョン情報をまとめたテキストファイルを作ってみましょう。ファイル名はversions.txtでansible-playbookコマンドを実行した環境に作成します。各行に環境名とバージョン情報を整形して「<環境名>: <パッケージ名>-<バージョン番号>」のように出力します。
この仕様で動作するAnsible Playbookは次のようになります。
- hosts: webserver tasks: - name: パッケージ情報を収集する ansible.builtin.package_facts: - name: パッケージ情報を記録する (Rocky Linux) ansible.builtin.lineinfile: path: versions.txt regexp: "^{{ inventory_hostname }}:" line: "{{ inventory_hostname }}: {{ item.name }}-{{ item.version }}" create: yes loop: "{{ ansible_facts.packages['nginx'] }}" when: ansible_distribution != "Rocky" delegate_to: localhost - name: パッケージ情報を記録する (Rocky Linux 以外) ansible.builtin.lineinfile: path: versions.txt regexp: "^{{ inventory_hostname }}:" line: "{{ inventory_hostname }}: {{ item.name }}-{{ item.version }}-{{ item.release }}" create: yes loop: "{{ ansible_facts.packages['nginx'] }}" when: ansible_distribution == "Rocky" delegate_to: localhost
バージョン情報をテキストファイルに出力するためansible.builtin.lineinfileモジュールを利用しています。出力する内容は前述の通りRocky LinuxとUbuntuで分ける必要があるため、whenキーワードを使ってOSごとに出力する内容を分けています。また、ansible-playbookコマンドを実行する環境のローカル上に情報を出力したいので、delegate_toキーワードを使って実際に実行する環境を指定しています。
上記のAnsible Playbookを実行すると、ansible-playbookコマンドを実行した環境のカレントパスにpackages.txtというファイルが作成され、以下のような内容が記録されます。
ubuntu2204: nginx-1.18.0-6ubuntu14.3 rocky9: nginx-1.20.1-10.el9
バージョン情報をチェックできたので、今度はパッケージのアップデートをAnsibleで実行してみましょう。本記事の執筆時点で、Rocky Linux向けのnginxパッケージのバージョンが上がり、nginx-1.20.1-13.el9がリリースされていました。このバージョンを指定してアップデートするAnsible Playbookを作成します。
作成するAnsible Playbookの内容は以下のようになります。
- hosts: webserver become: yes vars: nginx_version: Rocky: '1:1.20.1-13.el9' Ubuntu: '1.18.0-6ubuntu14.3' tasks: - name: 指定したバージョンの nginx にアップデートする (Rocky Linux) ansible.builtin.dnf: name: "nginx-{{ nginx_version.Rocky }}" state: present when: ansible_distribution == "Rocky" - name: 指定したバージョンの nginx にアップデートする (Ubuntu) ansible.builtin.apt: name: "nginx={{ nginx_version.Ubuntu }}" update_cache: yes state: present when: ansible_distribution == "Ubuntu"
まず、アップデート先となるnginxのバージョンを変数として定義し、今後さらにアップデートする作業が発生した場合に備えます。Rocky Linux向けのパッケージバージョン指定にepochの"1"を指定していますが、これはRocky Linux向けのnginxパッケージにepochが指定されているためです。
このAnsible Playbookを実行すると、指定したバージョンのパッケージにアップデートされます。今回は対象のシステムが2台だけでしたが、数十台、数百台のシステムが対象でも、指定したバージョンのパッケージに一度にアップデートできるため、素早い脆弱性対応が可能になるといえます。
次回は、インフラ自動化ツールの一つである「Terraform」をAnsibleと組み合わせて、Amazon Web Services(AWS)環境の構築を自動化する方法を紹介します。
サイオステクノロジー所属。OSS よろず相談室でサポート対応をしているテクニカルサポートエンジニア。Ansible に出会ってから自動化に取り憑かれ、自身の業務やプライベートであらゆるものの自動化に取り組む。プライベートでは Java でちょっとしたツールの開発を趣味にしている。
Copyright © ITmedia, Inc. All Rights Reserved.