こんにちは。SREチーム インフラエンジニアの綿引です。

今回は Ansible で SSL証明書更新を自動化したみたというお話です。

SSL証明書の更新って時間がかかりますよね。。後ヒューマンエラーも怖い。。
そこで 自動化 出来たら素敵!と思い vagrant で検証してみました。
興味のある方は是非見て頂ければと思います。

環境・構成

環境・構成は以下となっております。

■ サーバ側設定
・ ホスト名 : ans
・ IP : 172.19.12.101
・ 構成管理ツール : ansible/2.4.2.0
・ (配布用)証明書の保存ディレクトリ : /opt/ansible/ssl/

■ スレーブ側設定
・ ホスト名 : anc
・ IP : 172.19.12.102
・ WEBサーバ : Apache/2.4.6
・ 証明書の保存ディレクトリ : /etc/httpd/conf/ssl.crt/

■ 証明書情報
・ サーバ証明書ファイル名 : test.crt
・ 中間CA証明書ファイル名 : test.cer
・ 秘密鍵ファイル名 : test.key

やりたいこと

やりたいことの流れとしては、以下です。
1. ansible サーバ側に新しい SSL 証明書を配置
2. サーバ側でスクリプトをキック
3. サーバ側の新しい SSL 証明書を、クライアントに配布
4. その後、クライアント側の apache を reload して新しい証明書を適用

では早速やってみたいと思います。

まずAnsibleとは

その前にそもそも Ansible とは何ぞや、特徴は何だというのを簡単に、、

・構成管理ツール
➡︎ 同じようなプロダクトとしては chef や puppet などですね

・エージェントレス
➡︎ クライアント側にエージェントを入れなくて済むのはありがたい

・シンプル
➡︎ 他の構成管理ツールと比べると記述などがシンプル

個人的にはエージェントレスは嬉しい限りです。
対象サーバの台数が多いとエージェントをインストールするのも面倒ですし、
エージェントのインストールに引きずられて、他のパッケージやソースを入れることもないので。

Ansible のインストール

yum で Absible インストールします。くせもなく非常に簡単でした。

$ yum install epel-release
$ yum install ansible

$ ansible --version
ansible 2.4.2.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/vagrant/.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.5 (default, Jun 24 2015, 00:41:19) [GCC 4.8.3 20140911 (Red Hat 4.8.3-9)]

設定ファイルの作成

インストールも完了したので、次は設定を行っていきます。
実行に必要なファイルは(基本的に)以下の2つです。

■ インベントリファイル (hosts ファイル)
・操作したいリモートサーバのホスト名やIPを記述
・インベントリファイルに記載されたリモートサーバに対し操作が実行される
・尚、リモートサーバはグループ化することができ、そのグループに対し操作ができる

■ playbook(モジュール)
・yaml 形式
・リモートサーバに実行させたい処理を記述した定義ファイル
・Ansible の モジュール を使用し、ファイルコピーやサービス起動など様々な命令が可能

モジュールの数は現在 1,300 ほどあるみたいなので、かなり多くの要望に対応できそうです。
ただ普段使いするのはそこまで多くなさそう。。
今回使用したのは以下のモジュールです。

◆ 使用したモジュール(一部)

No. モジュール名 用途
1 file ディレクトリの作成や、対象ファイルの文字列置換
2 copy ファイルコピー
3 service サービス開始、停止
4 shell OSコマンドの実施

モジュールには他にも yum などがあり、
パッケージインストールも ansible にて可能なので
サーバの自動構築を行う際などに便利そうです。

インベントリファイル作成

まずはインベントリファイルから作成していきます。

適当に /opt 配下に ansible ディレクトリを作り、
その直下に hosts という名前のインベントリファイルを作成します。

$ sudo mkdir /opt/ansible/
$ cd /opt/ansible/
$ sudo vi hosts

先ほどのグループ指定の書き方をします。
“[ ]” 内にグループ名を記載し、参加させたいリモートサーバを羅列します。

[test1]
172.19.12.102

この記載だとtest1を選べば、第4オクテット 102 (ancサーバ) に対し実行します。
またホスト名で記載することも可能です。

もちろん以下のように複数台を選択することも可能です。

[test2]
172.19.12.103
172.19.12.104

yaml ファイル作成

次はクライアント側の操作を定義する yaml ファイルを作成します。

$ cd /opt/ansible/
$ sudo vi ssl_update.yaml

今回はこのような形で書いてみました。(汚いですが、、)
また変数を使いたかったため、vars_files というモジュールを使用しております。
vars_files は変数を定義したファイルを外に出すことができるので重宝しました。

- hosts: test1
  remote_user: vagrant
  become: yes
  vars_files:
   - /opt/ansible/vars.yml
  tasks:

  - name: 1. create backupdir
    file: path={{ dest_ssl_dir }}/{{ date }} state=directory owner=root group=root mode=0644

  - name: 2. backup file
    shell: cp -p {{ dest_ssl_dir }}/{{ crt_file }} {{ dest_ssl_dir }}/{{ date }}/{{ crt_file }};
           cp -p {{ dest_ssl_dir }}/{{ ica_file }} {{ dest_ssl_dir }}/{{ date }}/{{ ica_file }};
           cp -p {{ dest_ssl_dir }}/{{ key_nopw_file }} {{ dest_ssl_dir }}/{{ date }}/{{ key_nopw_file }}

  - name: 3. check file
    shell: diff -s {{ dest_ssl_dir }}/{{ crt_file }} {{ dest_ssl_dir }}/{{ date }}/{{ crt_file }};
           diff -s {{ dest_ssl_dir }}/{{ ica_file }} {{ dest_ssl_dir }}/{{ date }}/{{ ica_file }};
           diff -s {{ dest_ssl_dir }}/{{ key_nopw_file }} {{ dest_ssl_dir }}/{{ date }}/{{ key_nopw_file }}

  - name: 4. update crt_file
    copy: src={{ new_ssl_dir }}/{{ crt_file }} dest={{ dest_ssl_dir }}/{{ crt_file }} owner=root group=root mode=0644

  - name: 5. update ica_file
    copy: src={{ new_ssl_dir }}/{{ ica_file }} dest={{ dest_ssl_dir }}/{{ ica_file }} owner=root group=root mode=0644

  - name: 6. update key_nopw_file
    copy: src={{ new_ssl_dir }}/{{ key_nopw_file }} dest={{ dest_ssl_dir }}/{{ key_nopw_file }} owner=root group=root mode=0644

  - name: 7. httpd configcheck
    shell: httpd -t

  - name: 8. httpd reload for CentOS 7
    service: name=httpd state=reloaded
    when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "7")

  - name: 9.httpd reload for CentOS 56
    shell: /etc/init.d/httpd graceful
    when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "5") or
          (ansible_distribution == "CentOS" and ansible_distribution_major_version == "6")

yaml ファイルの内容ですが、各コメントの部分は以下の通りです。

項目 内容
1. create backupdir 証明書バックアップ用のディレクトリを作成(YYYYMMDD)
2. backup file 証明書のバックアップを取得
3. check file 現在のファイルとバックアップに差分がないか確認 (※1)
4. update crt_file サーバ証明書ファイルを更新
5. update ica_file 中間CA証明書ファイルを更新
6. update key_nopw_file 秘密鍵ファイルを更新
7. httpd configcheck Apache の configcheck を行う (※2)
8. httpd reload for CentOS 7 apache の reload が実行される (※3)
9. httpd reload for CentOS 56 apache の reload が実行される (※4)

※1 ・・・ 差分があった場合はdiffの返り値が “0” 以外となるためエラーとなる
※2 ・・・ httpd.conf の記述に問題がある場合は返り値が ”0” 以外となるためエラーとなる
※3 ・・・ CentOS 7 systemctl reload htttpd が実行される
※4 ・・・ CentOS 5 か 6 だった場合に/etc/init.d/httpd graceful が実行される

最後に /opt/ansible/vars.yml の中身です。

date: "{{ lookup('pipe','date +%Y%m%d') }}"
crt_file: test.crt
ica_file: test.cer
key_nopw_file: test.key
new_ssl_dir: /opt/ansible/ssl
dest_ssl_dir: /etc/httpd/conf/ssl.crt

実行

ansible を実行するにはansible-playbookコマンドを実行します。

(使い方例) : $ ansible-playbook -i hostsファイル yamlファイル

$ ansible-playbook -i hosts ssl_update.yaml

PLAY [test1] ********************************************************************************************************************************************************************************************************

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

TASK [1. create backupdir] ******************************************************************************************************************************************************************************************
changed: [anc]

TASK [2. backup file] ***********************************************************************************************************************************************************************************************
changed: [anc]

TASK [3. check file] ************************************************************************************************************************************************************************************************
changed: [anc]

TASK [4. update crt_file] *******************************************************************************************************************************************************************************************
ok: [anc]

TASK [5. update ica_file] *******************************************************************************************************************************************************************************************
ok: [anc]

TASK [6. update key_nopw_file] **************************************************************************************************************************************************************************************
ok: [anc]

TASK [7. httpd configcheck] *****************************************************************************************************************************************************************************************
changed: [anc]

TASK [8. httpd reload for CentOS 7] *********************************************************************************************************************************************************************************
changed: [anc]

TASK [9.httpd reload for CentOS 56] *********************************************************************************************************************************************************************************
skipping: [anc]

PLAY RECAP **********************************************************************************************************************************************************************************************************
anc                        : ok=9    changed=5    unreachable=0    failed=0

実行後、ブラウザで証明書の更新日を確認したら問題なく更新されておりました。成功です。
hostsファイルのグループ分けやディレクトリ構成、yamlファイルの記載など、
まだまだブラッシュアップが必要ですが一旦使えそうです。

まとめ

今回は Ansible で SSL 証明書をアップデートしました。
自身の環境でそこそこ上手くいったので、サービスに載せられないか検討中です。
皆さまもまだ Ansible 使ったことないんだよねーという方がいらっしゃれば、
参考にして頂けますと幸いです。

ご清覧頂きありがとうございました。
Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。
興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。

ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!

Join Us !

ウエディングパークでは、一緒に働く仲間を募集しています!
ご興味ある方は、お気軽にお問合せください(カジュアル面談から可)

採用情報を見る