この記事は Ansible Advent Calendar 2017 12/8(金)の記事になります。
(前日までに書いておこうと思ってたんですが当日早朝に慌ててるのは仕様です、すみません・・・)
実は今回の記事でQiita初投稿だったりします。緊張で手が震え、筆を握れません。なのでmacのキーボードを叩いて文章を書いていこうと思います。(きっと筆で書いたら誰も読めない汚らしい文章になることは保証します)
で、参加表明してからネタを考えるべく Ansible の Document を眺めていたら、Crypto Modules なんてモジュールが追加されているのに気がついたので、これをネタにすることにしました。
オレオレ証明書をAnsibleで作ろう!
開発とかデモとかちょっとしたお試しでSSL通信したいなってときにopensslを使ってオレオレ証明書を作ると思います。まぁ大した手間ではないと言えばないですが、コード化して置いたらちょっとは楽になれるかな〜と思って試してみました。
オレオレ証明書作成の流れ
わざわざ説明するまでも無いですが、オレオレ証明書を作成する場合、自身でprivateキーの作成と証明書要求を作成し、自分のprivateキーを使って認証することでオレオレ証明書が完成します。
今回はもう一歩踏み込んで、オレオレ証明書により認証局を作って、そこから証明書を発行するまで目指します。
Crypto Modules でそれぞれの操作を実施するモジュールが用意されているので、これを使って作成します。
コードは GitHub に突っ込んでおくので興味ある方は使ってみてください。(デバッグお願いしm ry)
以下コードをみていきます。
事前準備
設定ファイルから見ていきます。
[ca_host]
cahost ansible_host=localhost remote_user=centos
[target_hosts]
target01 ansible_host=localhost remote_user=centos
まずはイベントリーファイルですが、あまり語ることはありませんw
ca_host グループのホストを認証局に仕立て上げるつもりです。
実際に証明書の発行を行うのは target_hosts グループのホストになります。
なお、ca_hostに2つ以上ホスト書かれるのは想定外なのでやめてください...
コードのテストはCentOS7環境で行ってます。ubuntuでも動くかな〜。packageモジュールでパッケージインストール書いておいたのでapt-getの方も裏で動くと良いのですがw
次にgroup_varsを設定していきます。
---
# certificate store dir of playbook playing host
local_store_dir: ~/certificate_store
# certificate file store dir of remote hosts
remote_store_dir: /etc/pki/mycertificates
# csr file store dir of ca host
ca_csr_store_dir: /etc/pki/csr_store
証明書等のファイルの保存先などを指定しています。
local_store_dir
ansible実行ホスト上で成果物を保存するディレクトリ
remote_store_dir
証明書を発行する各ホストで成果物を保存するディレクトリ
ca_csr_store_dir
オレオレ認証局ホストの証明書要求等の保存先
なお、オレオレ認証局の証明書等は面倒なのでopensslで初期に作成されるので /etc/pki/CA以下に保存先を固定してます。
オレオレ認証局の情報を設定します。
---
#commonName
ca_CN: myCA.hogehoge.hoge
#countryName
ca_C: JP
#emailAddress
ca_EMail: hogehoge@hogehoge.hoge
#localityName
ca_L: Shinagawa
#organizationName
ca_O: my company
#organizationalUnitName
ca_OU: my division
#stateOrProvinceName
ca_ST: Tokyo
証明書でお馴染みの属性なので、適宜いじってください。
実際に発行するサーバ証明書の方は以下で設定します。
---
#commonName
target_CN: hoge.hogehoge.hoge
#countryName
target_C: JP
#emailAddress
target_EMail: hoge@hogehoge.hoge
#localityName
target_L: Shinagawa
#organizationName
target_O: my company
#organizationalUnitName
target_OU: my division
#stateOrProvinceName
target_ST: Tokyo
こちらもパラメータとしては同じですね。
複数台同時に処理するなら適宜これをコピーして作ってくださいな。
オレオレ認証局の作成
まずは認証局作成のPlaybookを実行してください。
ansible-playbook -i inventory/inventory.ini ca_setup.yml
以下、簡単に処理を見ていきます。
tasks:
- name: ensure openssl installed
package:
name: openssl, python
state: latest
- name: ensure pip installed
shell: which pip
failed_when: result.rc < 0
register: result
- block:
- name: download get-pip.py
get_url:
dest: ~/get-pip.py
url: https://bootstrap.pypa.io/get-pip.py
- name: install pip
shell: python ~/get-pip.py
when: result.rc == 1
- name: ensure pyOpenSSL installed
pip:
name: pyOpenSSL
state: present
Crypto Modulesの必要用件のセットアップですね。
pythonの最近のバージョンだとpip含まれてるかとは思いますが今回はget-pip.pyで入れるようにしています。
- name: ensure created selfca private key
openssl_privatekey:
path: /etc/pki/CA/private/cakey.pem
state: present
オレオレ認証局の秘密鍵を作ります。
- name: ensure created csr
openssl_csr:
commonName: "{{ ca_CN }}"
countryName: "{{ ca_C }}"
emailAddress: "{{ ca_EMail }}"
localityName: "{{ ca_L }}"
organizationName: "{{ ca_O }}"
organizationalUnitName: "{{ ca_OU }}"
path: /etc/pki/CA/cacert.csr
privatekey_path: /etc/pki/CA/private/cakey.pem
state: present
stateOrProvinceName: "{{ ca_ST }}"
証明書要求を作成して・・・
- name: ensure self sighed certificate
openssl_certificate:
csr_path: /etc/pki/CA/cacert.csr
path: /etc/pki/CA/cacert.pem
privatekey_path: /etc/pki/CA/private/cakey.pem
provider: selfsigned
state: present
valid_in: 315360000
自己サインします。期限は10年にしておきます(秒数なのでこんな数字に)
- name: ensure CA cert convert to DER for client
shell: openssl x509 -inform PEM -outform DER -in /etc/pki/CA/cacert.pem -out /etc/pki/CA/MyCAcert.der
args:
creates: /etc/pki/CA/MyCAcert.der
サーバにimportするための認証局証明書をDERで用意しておきます。
- name: ensure index file created
file:
path: /etc/pki/CA/index.txt
state: touch
- name: ensure serial file created
shell: echo "01" > /etc/pki/CA/serial
args:
creates: /etc/pki/CA/serial
認証局の初期設定で必要なファイル。シリアル番号はまぁ任意で変えてもいいと思います。
- name: ensure download CA cert
fetch:
dest: "{{ local_store_dir }}/MyCAcert.der"
fail_on_missing: yes
flat: yes
src: "/etc/pki/CA/MyCAcert.der"
認証局証明書のDERを取得しておきます。
以上でオレオレ認証局はできました。
サーバ側で証明書要求を準備
次はサーバ側で証明書要求を作成していきます。
ansible-playbook -i inventory/inventory.ini gen_csr.yml
以下、簡単に見ていきます。
tasks:
- name: ensure openssl installed
package:
name: openssl, python
state: latest
- name: ensure pip installed
shell: which pip
failed_when: result.rc < 0
register: result
- block:
- name: download get-pip.py
get_url:
dest: ~/get-pip.py
url: https://bootstrap.pypa.io/get-pip.py
- name: install pip
shell: python ~/get-pip.py
when: result.rc == 1
- name: ensure pyOpenSSL installed
pip:
name: pyOpenSSL
state: present
オレオレ認証局の時と同じく必要パッケージの導入。
- name: ensure store fir exist
file:
mode: 0664
path: "{{ remote_store_dir }}"
state: directory
成果物の保存先ディレクトリを作っておきます。
パラメータ名わかりづらいなこれ・・・
- name: ensure created target hosts private key
openssl_privatekey:
path: "{{ remote_store_dir }}/{{ inventory_hostname }}_key.pem"
state: present
秘密鍵を作って・・・
- name: ensure created csr
openssl_csr:
commonName: "{{ target_CN }}"
countryName: "{{ target_C }}"
emailAddress: "{{ target_EMail }}"
localityName: "{{ target_L }}"
organizationName: "{{ target_O }}"
organizationalUnitName: "{{ target_OU }}"
path: "{{ remote_store_dir }}/{{ inventory_hostname }}.csr"
privatekey_path: "{{ remote_store_dir }}/{{ inventory_hostname }}_key.pem"
state: present
stateOrProvinceName: "{{ target_ST }}"
証明書要求を作ったら・・・
- name: ensure fetch csr file
fetch:
dest: "{{ local_store_dir }}/{{ inventory_hostname }}.csr"
fail_on_missing: yes
flat: yes
src: "{{ remote_store_dir }}/{{ inventory_hostname }}.csr"
ダウンロードして終わりです。
オレオレ認証局で署名
いよいよ本題のサーバ証明書発行です。
ansible-playbook -i inventory/inventory.ini gen_cert.yml
例によって処理を見ていきますよ。
playbookとして独立しちゃってるので前提条件のパッケージの所在確認とかあった方が良い気もしつつ、オレオレ認証局ができていることが前提でその辺の処理は省いちゃってます。本当はちゃんとやらんとあかんな・・・・
tasks:
- name: ensure store fir exist
file:
mode: 0664
path: "{{ ca_csr_store_dir }}"
state: directory
保存先ディレクトリの作成・確認。
- name: ensure copy csr files to ca site
copy:
dest: "{{ ca_csr_store_dir }}/{{ item }}.csr"
src: "{{ local_store_dir }}/{{ item }}.csr"
with_items: "{{ groups['target_hosts'] }}"
サーバ証明書要求をオレオレ認証局にアップロード。
- name: ensure sighed certificate
shell: openssl ca -cert /etc/pki/CA/cacert.pem -keyfile /etc/pki/CA/private/cakey.pem -in "{{ ca_csr_store_dir }}/{{ item }}.csr" -out "{{ ca_csr_store_dir }}/{{ item }}.cert.pem" -days 3650 -batch
with_items: "{{ groups['target_hosts'] }}"
署名します。これはAnsibleモジュールではできなさげでしたので普通にopensslコマンド叩きます。
- name: ensure fetch csr file
fetch:
dest: "{{ local_store_dir }}/{{ item }}.cert.pem"
fail_on_missing: yes
flat: yes
src: "{{ ca_csr_store_dir }}/{{ item }}.cert.pem"
with_items: "{{ groups['target_hosts'] }}"
証明書をダウンロードしておしまい。
ほんとはこの後サーバへアップロードする必要ありますがcopyモジュールでコピーして終わりなのでめんどいので割愛。
う〜ん、これ役に立つんですかね。ただのネタで終わる可能性も。
とりあえず個人的には色々なデモ環境構築のコードで使いまわすことにします。
さて、これ書くために我慢したPS4モンハンエディションのセットアップが待ってるのでこの辺で失礼いたします・・・・(いや、仮眠しよう・・・)