2021.6.29
はじめに
脆弱性には速やかな対応が求められる
脆弱性には速やかな対応が求められます。 ベンダによって脆弱性情報が公開された後、攻撃者は脆弱性を分析し、攻撃コードを作成。その後、攻撃を開始します。 防御側はこの攻撃開始までに脆弱性への対応を行わなければなりません。 攻撃開始までの期間は年々短くなっており、脆弱性への対応は以前と比較してより短期間での実施が求められています。
Ansible® によるパッケージ更新の自動化
このエントリでは、Amazon Web Services (AWS) Systems Manager (旧称 SSM) の Ansible Playbook 実行機能を利用して、 AWS EC2 インスタンス (Amazon Linux 2 AMI/Ubuntu 20.04 LTS) のパッケージを更新する方法をご紹介します。
- 通常の Ansible 構成と AWS Systems Manager の Ansible Playbook 実行構成の違い
- 想定する環境
- マネージドインスタンスにする
- Ansible Playbook の用意
- Ansible Playbook の保存
- Ansible Playbook の実行
- 補足
- まとめ
Ansible とは、オープンソースの構成管理ツールの一種です。 エージェントレスのため、管理対象ノードに追加でインストールするソフトウェアは一部を除き不要です。(ssh 経由での接続は必要)
このエントリで扱う内容は、下図に示された脆弱性対策のフローの中で、「適用」フェーズに含まれるものです。
通常の Ansible 構成と AWS Systems Manager の Ansible Playbook 実行構成の違い
通常の Ansible の運用構成では、コントロールノードとターゲットノードが存在します。 Ansible は、コントロールノードにのみインストールされ、操作対象となるターゲットノードにはインストールされません。 コントロールノード上で実行された Playbook は Python 実行コードなどに変換されターゲットノードへ送信、実行されます。
AWS Systems Manager による Ansible Playbook の実行では、操作対象となる EC2 インスタンス (図中のターゲットインスタンス) に Ansible がインストールされます。 Playbook の実行では、まずターゲットインスタンスから AWS Systems Manager にポーリングを行い、 Run Command やステートマネージャーの SSM ドキュメント (AWS-ApplyAnsiblePlaybooks) に定義された処理を取得します。 次に、SSM ドキュメントに指定されたダウンロード元 (ここでは Amazon S3 バケット) から Playbook をダウンロードします。 最後に、ダウンロードした Playbook をターゲットインスタンス上で実行します。
違いをまとめると次の表のようになります。
通常 | AWS Systems Manager | |
---|---|---|
Ansible のインストール先 | コントロールノード | ターゲットノード |
ターゲットの指定 | コントロールノード | AWS Systems Manager |
Playbook の保存場所 | コントロールノード | Amazon S3 または Github |
想定する環境
想定する環境を下図に示します。
Amazon Virtual Private Cloud (Amazon VPC) 内に Public subnet と Private subnet があり、 Public subnet はインターネットからのアクセスが可能なサブネットです。 一方、Private subnet はインターネットからのアクセスはできないサブネットとなっています。 Ansible Playbook を実行する対象である Amazon EC2 インスタンスは Private subnet 内に配置されています。 EC2 インスタンスの OS は Amazon Linux 2 AMI と Ubuntu Server 20.04 LTS とします。 これらの EC2 インスタンスは、SSH も含め、外部からアクセスできませんが、EC2 インスタンスからインターネットへの通信は Public サブネット内に設置された NAT Gateway を経由してインターネットへ接続できることとします。 同様に、EC2 インスタンスから AWS Systems Manager への通信も NAT Gateway 経由で可能であるとします。
マネージドインスタンスにする
AWS Systems Manager の Ansible Playbook 実行機能を利用するために、マネージドインスタンスにします。 マネージドインスタンスにするためには、次の要件を満たす必要があります。
- AWS Systems Manager Agent (以下、SSM Agent とします) の導入
- AWS Systems Manager API (以下、SSM API とします) への通信経路の確保
- IAM ロールの付与
以下、順にご説明します。
-
SSM Agent の導入
SSM Agent とは SSM API と連携し、リソース (EC2 インスタンス、オンプレミスサーバー、あるいは仮想マシン) の更新、管理、設定を行うためのソフトウェアです。 AWS Systems Manager 経由で Ansible Playbook を実行する際にも SSM Agent が必要です。 SSM Agent は、Amazon Linux や Ubuntu Server のオフィシャルイメージには導入済みとなっています。 このエントリでは SSM Agent 導入済みのオフィシャルイメージを使用いたします。
-
SSM API への通信経路の確保
SSM Agent から SSM API へのアウトバウンド通信の経路を確保します。 今回は NAT Gateway 経由で AWS Systems Manager に接続できるようにネットワークを構成していますが、VPN エンドポイント経由などでも可能です。 ご利用の AWS/オンプレミス 環境に適した方法で、SSM API への通信経路を確保してください。
-
IAM ロールの付与
AWS Systems Manager へのアクセスを許可するため、IAM ロールを作成して EC2 インスタンスに付与します。 今回は管理コンソールの Identity and Access Management (IAM) から新たに IAM ロールを作成し、以下の IAM ポリシーをアタッチします。
-
AmazonSSMManagedInstanceCore
: AWS Systems Manager へのアクセスのため (必須) -
AmazonS3ReadOnlyAccess
: Ansible Playbook を保存している S3 バケットへのアクセスのため (オプション)
EC2 インスタンスを新規に作成する場合は、新たに作成した IAM ロールを指定してください。
既存の EC2 インスタンスの IAM ロールを変更したりアタッチする場合には こちら のエントリをご参照ください。
-
Ansible Playbook の用意
今回 Amazon Linux 2 AMI および Ubuntu Server 20.04 LTS のパッケージを全て最新にする Ansible Playbook を作成します。 なお、ここでは全てのパッケージを対象にしていますが、実際の運用では特定のパッケージのみ更新することのほうが多いかと思われますので、更新するパッケージは適宜変更してください。
-
ディレクトリ
upgrade_packages
を作成し、必要な Playbook を配置します。upgrade_packages
の構造は以下のとおりです。(※ ディレクトリレイアウトは Ansible のベストプラクティスに従ったものになっております。)upgrade_packages/
├── common
│ └── tasks
│ └── main.yml
└── server.yml
-
server.yml
は通常の Ansible の実行時に
$ ansible-playbook -i xxx server.yml
server.yml
の内容を見てみましょう。server.yml:
- name: upgrade all packages
hosts: all
become: true
become_method: sudo
roles:
- common
hosts: all
となっています。 後述するように、AWS Systems Manager の設定でターゲットとなる EC2 インスタンスやそのグループを指定するので、hosts の項目は特に意味を持ちません。 この点にご注意ください。 -
パッケージ更新を行う
common/tasks/main.yml
も見てみましょう。main.yml:
---
- name: gather ec2 facts
ec2_metadata_facts:
- name: upgrade all packages (Red Hat)
yum:
name: '*'
state: latest
when: ansible_os_family == "RedHat"
- name: upgrade all packages (Debian)
apt:
name: '*'
state: latest
when: ansible_os_family == "Debian"
-
upgrade_packages
ディレクトリを zip ファイルにまとめます。 この zip ファイルを Amazon S3 や Github に保存します。 EC2 インスタンスでの Playbook 実行時には、zip ファイルの展開も自動に行われます。
Ansible Playbook の保存
Amazon S3 への保存
S3 バケットに upgrade_packages.zip をアップロードします。
Github への保存
Github の適当なリポジトリに upgrade_packages.zip をアップロードします。
Ansible Playbook の実行
Ansible Playbook は Run Command や ステートマネージャー から実行可能です。 Run Command は一度限りの処理を実行する場合に、ステートマネージャーは定期的に処理を行いたい場合に便利です。 それぞれの設定方法を以下に説明します。
Run Command による実行
-
AWS Systems Manager のトップ画面の左メニューから [Run Command] をクリックします。
-
画面右上の [Run Command] ボタンをクリックします。
-
使用する SSM ドキュメントを指定します。
ラジオボタンからAWS-ApplyAnsiblePlaybooks
ドキュメントを選択してください。 -
次に、コマンドのパラメータを指定します。
-
[Source Type] は Ansible Playbook のダウンロード元を指定する項目です。 [Github] あるいは [S3] を選びます。
-
[Source Info] では、Ansible Playbook のファイルへのパス等、より詳細な情報を JSON 形式で指定します。
例1: Amazon S3 に保存された zip ファイルを指定します
{
"path":"
https://bucket_name.s3.ap-northeast-1.amazonaws.com/upgrade_packages.zip
"
}
例2: Github に保存された zip ファイルを指定します
{
"owner":"
owner_name
",
"repository":"
repository_name
",
"path":"
upgrade_packages.zip
",
"getOptions":"
branch:branch_name
"
}
-
[Install Dependencies] を [True] にした場合、Ansible など必要なパッケージが EC2 インスタンスにインストールされます。 インストールされるパッケージについては こちら の「インストールされている依存関係」を参照してください。
-
[Playbook File] (オプション) に実行する Playbook のパスを指定します。
-
[Extra Variables] (オプション) にはAnsible 実行時に渡す追加の変数を記載します。
-
[Check] (オプション) を [True] にした場合、チェックモード (ドライラン) で実行されます。
-
[Verbose] (オプション) では、ログレベルを指定します。ログレベルは -v、-vv、-vvv、-vvvv から選択できます。(Ansible 実行時に渡すログレベルと同じです)
-
[Timeout Seconds] (オプション) ではタイムアウトの時間を秒単位で指定します。
-
-
Ansible Playbook を実行するターゲットを指定します。ターゲットの選択方法は次のとおりです。
- [インスタンスタグの指定]
- [インスタンスを手動で選択する]
- [リソースグループの選択]
hosts
キーワードなどの代わりに、このセクションを使用してターゲットを指定します。 インスタンスタグやリソースグループによってグループ化されたインスタンスを選択できるので、 実行対象をテスト系や運用系で分けたり、データベースを含むインスタンスなど特定の機能で分類されたグループをターゲットにするなど、 柔軟な運用が可能です。 -
レート制限 セクションでは、 [同時実行数] (Ansible の
serial
キーワードに相当) や [エラーのしきい値] を設定できます。 -
出力オプション セクションでは、 実行時の出力の Amazon S3 バケットへの書き込みや CloudWatch への書き込みを指定できます。 (Amazon S3 バケットへの書き込みに必要なアクセス権限については こちら を参照してください。)
-
[Run] ボタンをクリックし、指定した処理を実行します。
-
実行中の処理について、全体的なステータスやインスタンス毎のステータスが表示されます。
-
処理が終了した際のステータス画面です。もし失敗した場合はインスタンスのリンクを辿ることでエラーなどの出力を確認できます。
ステートマネージャーからの実行
-
AWS Systems Manager のトップ画面の左メニューから [ステートマネージャー] をクリックします。
-
画面右上の [関連付けの作成] ボタンをクリックします。
-
使用する SSM ドキュメントを指定します。
ラジオボタンからAWS-ApplyAnsiblePlaybooks
ドキュメントを選択してください。 -
コマンドのパラメータ セクションは Run Command による実行と同じため、ここでは説明を省略いたします。
-
Ansible Playbook を実行するターゲットを指定します。ターゲットの選択方法は次のとおりです。
- [インスタンスタグの指定]
- [インスタンスを手動で選択する]
- [リソースグループの選択]
hosts
キーワードなどの代わりに、このセクションを使用してターゲットを指定します。 インスタンスタグやリソースグループによってグループ化されたインスタンスを選択できるので、 実行対象をテスト系や運用系で分けたり、データベースを含むインスタンスなど特定の機能で分類されたグループをターゲットにするなど、 柔軟な運用が可能です。 -
スケジュールを指定 セクションでは、定期実行や日時を指定した実行の指定が可能です。
-
レート制限 セクションでは、 [同時実行数] (Ansible の
serial
キーワードに相当) や [エラーのしきい値] を設定できます。 -
出力オプション セクションでは、 実行時の出力の Amazon S3 バケットへの書き込みや CloudWatch への書き込みを指定できます。 (Amazon S3 バケットへの書き込みに必要なアクセス権限については こちら を参照してください。)
-
[関連付けの作成] ボタンをクリックします。
-
作成した関連付けのステータスが表示されます。図ではまだ処理が行われておらず、ステータスが「保留中」になっています。
-
初回の処理が終了した際のステータス画面です。もし失敗した場合はインスタンスのリンクを辿ることでエラーなどの出力を確認できます。
補足
Ansible のバージョンについて
前述したように、AWS Systems Manager による Ansible Playbook 実行ではターゲットとなる EC2 インスタンスに Ansible がインストールされます。 なおインストールされる Ansible のバージョンは、EC2 インスタンスの OS によって異なります。
ここでは、AWS Systems Manager によってインストールされた Ansible のバージョンを見てみましょう。
まず Amazon Linux 2 AMI を確認します。
$ ansible --version
ansible 2.9.21
config file = /etc/ansible/ansible.cfg
configured module search path = [u'/home/ssm-user/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.7/site-packages/ansible
executable location = /usr/bin/ansible
python version = 2.7.18 (default, Feb 18 2021, 06:07:59) [GCC 7.3.1 20180712 (Red Hat 7.3.1-12)]
次に Ubuntu Server 20.04 LTS も確認します。
$ ansible --version
ansible [core 2.11.1]
config file = None
configured module search path = ['/home/ssm-user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python3.8/dist-packages/ansible
ansible collection location = /home/ssm-user/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible
python version = 3.8.5 (default, May 27 2021, 13:30:53) [GCC 9.3.0]
jinja version = 2.10.1
libyaml = True
Amazon Linux 2 AMI では 2.9 系が、 Ubuntu Server 20.04 LTS では 2.11 系が インストールされていることがわかります。(バージョンは 2021年06月23日 現在のものです。今後は変更される可能性があります。)
ご存知のように、Ansible は 2.10 で大幅な仕様変更があり、2.9 までは ansible の公式に含まれていたモジュールの大部分が外部のモジュールとして切り出されました。 また Playbook の記述方法も変更されています。そのため、2.9 では動作していた Playbook が 2.10 以降では必要なモジュールが足りないなどの理由で動作しないといったことも発生しています。
AWS Systems Manager を利用して Ansible Playbook を実行する際には、EC2 インスタンスの Ansible バージョンを把握しておくことをお勧めします。
まとめ
AWS Systems Manager の Ansible Playbook 実行機能を利用して EC2 インスタンスのパッケージを更新する方法をご紹介しました。
通常の Ansible 構成、つまりオンプレミスや AWS Cloud にコントロールノードを配置してターゲットノードに接続する方法では、
- (ダイナミックインベントリを利用したとしても) インベントリの管理
- EC2 インスタンスへの通信経路の確保
- EC2 インスタンスへの SSH アクセスの確保
などに頭を悩ませられることが多いかと思います。AWS Systems Manager の Ansible Playbook 実行機能は、これらの悩みを解消できる点が優れています。 またステートマネージャーを使用することで、Ansible Playbook の実行タイミングを指定できますので、Ansible Engine そのものの弱点も補うことができます。
一方で、コントロールノードを配置しない代償として、ターゲットになる EC2 インスタンスに Ansible やその依存関係をインストールしなければならないので、この点には注意が必要です。 また AWS Systems Manager のパッチマネージャーにはパッチ適用前後にフックを利用してスクリプトを実行するといった機能が用意されていますが、Ansible Playbook 実行ではそのような機能が見当たらないため、yum モジュールや apt モジュールによるパッチ適用前後の監視の無効化や有効化は Ansible Playbook に自前で用意する必要がありそうです。
最後に、最もお伝えしたい点ですが、
弊社の脆弱性管理ツール「SIDfm VM」には修正プログラムを適用するための Ansible Playbook を出力する機能があります
ので、このエントリでご紹介した方法と組み合わせることによって、管理がグッと楽になります。是非活用をご検討ください。
参照
- AWS Systems Manager (Amazon)
- チュートリアル: Ansible プレイブックを実行する関連付けの作成 (Amazon)
- GitHub からの Ansible プレイブックの実行 (Amazon)
- Running Ansible Playbooks using EC2 Systems Manager Run Command and State Manager (Amazon)
- Keeping Ansible effortless with AWS Systems Manager (Amazon)
- Automate Ansible playbook deployment with Amazon EC2 and GitHub (Amazon)