はじめに
インフラの構築は練り上げた手順書を使い、チェックシートを持った担当者に見守られながら、コンソールに指差し確認をして1つ1つコマンドを打ち込んでいくのが、古き良き時代の習わしでした。
しかし、近代化の波はDevOptsという造語とともにやってきて、私を含めたおじさんエンジニアは翻弄される今日この頃です。
ちょっと、冗談めいた出だしでしたが我が社でも少しずつツールを利用しながら、何度も繰り返す作業は効率のよい方法に置き換えていくことを進めています。
Ansibleはそのひとつで、弊社のプロダクトもひととおり自動で構築できるような体制が整っています。
今回は弊社製品ではないのですが、シングルサインオンに関わるプロダクトの1つであるShibboleth IdPをインストールするyamlの記述を実際に解説していこうと思います。
Ansibleのディレクトリ構成
Shibbolethの処理を説明する前にAnsibleのディレクトリ構成ですが、以下のように用意しました。
inventoriesは変数の定義したファイルを保存します。
localは環境を指していて、stagingやproductionなど状況に応じて環境別の設定が出来るようにしています。
group_varsはサーバ分類ごとのグループを指しています。
今回は「shibboleth-idp_groups」だけです。
このファイルにShibboleth IdPに関するほとんどの変数を定義しています。
host_varsはサーバホストごとの変数です。
今回は「shibb1」というホスト名で1台のみですが、冗長化する場合など複数台構成時にこのホストはマスターでこのホストはスレーブだというような振る舞いを変更したい場合に変数を定義したりします。
kvmはAnsibleと特に関係ありません。私がkvm上で仮想マシンを作成するためのファイル等を保存しています。
rolesが実際のサーバグループの構築処理が記載されています。
commonはサーバ共通で用意している処理ですが、内容は割愛します。
hostsファイルや時刻同期、ファイアウォールなどの設定を通常は記載しています。
shibboleth-idpが実際の構築処理ですが、細分化しています。
install: Shibboleth IdPのパッケージインストール
setup-idp: Shibboleth IdPのセットアップ
setup-mariadb: MariaDBのセットアップ
setup-middle: ミドルウェアのセットアップ
各ディレクトリにfilesやtemplatesがありますが、実際に格納されているファイルの表示は割愛しています。処理内容の詳細を見て準備してください。
ディレクトリ直下のファイルですが、
ansible.cfgがAnsibleの環境設定です。
README.mdはこのAnsible Playbookの内容説明です。
shibboleth-idp.ymlがrolesの実行手順が記載されています。
Shibboleth IdPの構築手順
では、実際に処理の記載方法を見ていきますしょう。
まず、Ansibleの実行コマンドは以下の通りです。
ansible-playbook -i inventories/local shibboleth-idp.yml
inventories/localの変数定義を使ってshibboleth-idp.ymlを実行します。
shibboleth-idp.ymlの内容は以下の通りです。
- hosts: shibboleth-idp_groups
roles:
- common
- shibboleth-idp/install
- shibboleth-idp/setup-middle
- shibboleth-idp/setup-mariadb
- shibboleth-idp/setup-idp
hostsはshibboleth-idp_groupsのグループ定義を使って、
roles以下の処理を順番に実行するという内容です。
次にhostsファイルの内容を見てみます。
[shibboleth-idp_groups]
shibb1 ansible_host=10.0.119.131
[local:children]
shibboleth-idp_groups
[local:vars]
domain=test.osstech.co.jp
env=local
project_name=Ansible_Template
[shibboleth-idp_groups]にはshibb1というホスト名で10.0.119.131のIPアドレスである記載があります。複数台のときはホスト行を増やして記載します。
[local:children]にはlocal環境にshibboleth-idp_groupsが属していることを記載します。
[local:vars]はlocal環境共通で利用する変数を記載します。
次にshibboleth-idp/installのロール処理です。
- name: Install Apache Java Tomcat rsync
yum:
name: "{{item}}"
state: latest
with_items:
- httpd
- mod_ssl
- java-1.8.0-openjdk
- jakarta-taglibs-standard
- tomcat
- rsync
tags:
- install-packages
- name: Send Shibboleth IdP Packages
copy:
src: "{{product_version.shibboleth_idp}}.tar.gz"
dest: "{{workdir}}/{{product_version.shibboleth_idp}}.tar.gz"
mode: "0644"
tags:
- install-packages
- name: Send MySQL Driver Packages
copy:
src: "{{product_version.mysql}}.tar.gz"
dest: "{{workdir}}/{{product_version.mysql}}.tar.gz"
mode: "0644"
tags:
- install-packages
- name: Extract Shibboleth IdP Packages
shell: >
cd {{workdir}} && tar xvf "{{product_version.shibboleth_idp}}.tar.gz";
tags:
- install-packages
- name: Jakarta taglibs link
shell: >
ln -s /usr/share/java/jakarta-taglibs-core.jar /usr/share/tomcat/lib/jakarta-taglibs-core.jar;
ln -s /usr/share/java/jakarta-taglibs-standard.jar /usr/share/tomcat/lib/jakarta-taglibs-standard.jar;
register: taglibs_link
failed_when: not "'File exists' in taglibs_link.strerr"
tags:
- install-packages
- name: Install MariaDB
yum:
name: "{{item}}"
state: latest
with_items:
- mariadb
- mariadb-server
- name: Extract MySQL Driver Packages
shell: >
cd {{workdir}} && tar xvf "{{product_version.mysql}}.tar.gz";
tags:
- install-packages
- name: Deploy MySQL Driver
shell: >
cp "{{workdir}}/{{product_version.mysql}}/{{product_version.mysql}}-bin.jar" "{{ tomcat.sharelibdir }}/";
chown tomcat:tomcat "{{ tomcat.sharelibdir }}/{{product_version.mysql}}-bin.jar";
tags:
- install-packages
内容はyumモジュールでApahce,Java,Tomcatなどのインストールをして、
shellモジュールでShibboleth IdP、MySQLコネクタのjavaドライバを送信してインストールしています。
次にshibboleth-idp/setup-middleのロール処理です。
- name: Check httpd.conf
stat: path=/etc/httpd/conf/httpd.conf.org
register: httpd_conf
- name: Backup httpd.conf
shell: mv /etc/httpd/conf/httpd.conf /etc/httpd/conf/httpd.conf.org
when: httpd_conf.stat.md5 is not defined
- name: Check conf.d
stat: path=/etc/httpd/conf.d.org
register: httpd_confd
- name: Backup conf.d
shell: mv /etc/httpd/conf.d /etc/httpd/conf.d.org
when: not httpd_confd.stat.exists
- name: Mkdir conf.d
file: path=/etc/httpd/conf.d state=directory owner=root group=root mode=0755
- name: Deploy httpd.conf
template:
src: httpd.conf.j2
dest: /etc/httpd/conf/httpd.conf
mode: "0644"
owner: "root"
group: "root"
- name: Deploy shibboleth.conf
template:
src: shibboleth.conf.j2
dest: /etc/httpd/conf.d/shibboleth.conf
mode: "0644"
owner: "root"
group: "root"
- name: Deploy ssl.conf
template:
src: ssl.conf.j2
dest: /etc/httpd/conf.d/ssl.conf
mode: "0644"
owner: "root"
group: "root"
- name: Change tomcat.conf
shell: >
sed -i '$ a \\nCATALINA_OPTS="-Xmx2048M -Xms2048M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M"' /usr/share/tomcat/conf/tomcat.conf;
register: change_tomcat_conf
changed_when: false
- name: Change tomcat.conf Result
debug: var=change_tomcat_conf.stdout_lines
- name: Deploy LDAP Certificate
copy:
src: "{{certificate.ldap_cacertfile}}"
dest: "{{certificate.ca_anchors_dir}}/"
mode: "0644"
owner: root
group: root
- name: LDAP Certificate to OS
shell: >
update-ca-trust extract;
- name: Deploy server.xml
template:
src: server.xml.j2
dest: /usr/share/tomcat/conf/server.xml
mode: "0644"
owner: "root"
group: "tomcat"
- name: Change context.xml
shell: >
sed -i /usr/share/tomcat/conf/context.xml -e 's/<Context>/<Context useHttpOnly="true">/';
register: change_context_xml
changed_when: false
- name: Change context.xml Result
debug: var=change_context_xml.stdout_lines
- name: Stop Tomcat Logrotate
shell: >
sed -i /usr/share/tomcat/conf/logging.properties -e 's/1catalina.org.apache.juli.FileHandler.prefix = catalina./1catalina.org.apache.juli.FileHandler.prefix = catalina.\n1catalina.org.apache.juli.FileHandler.rotatable = false/';
sed -i /usr/share/tomcat/conf/logging.properties -e 's/2localhost.org.apache.juli.FileHandler.prefix = localhost./2localhost.org.apache.juli.FileHandler.prefix = localhost.\n2localhost.org.apache.juli.FileHandler.rotatable = false/';
sed -i /usr/share/tomcat/conf/logging.properties -e 's/3manager.org.apache.juli.FileHandler.prefix = manager./3manager.org.apache.juli.FileHandler.prefix = manager.\n3manager.org.apache.juli.FileHandler.rotatable = false/';
sed -i /usr/share/tomcat/conf/logging.properties -e 's/4host-manager.org.apache.juli.FileHandler.prefix = host-manager./4host-manager.org.apache.juli.FileHandler.prefix = host-manager.\n4host-manager.org.apache.juli.FileHandler.rotatable = false/';
cat /usr/share/tomcat/conf/logging.properties;
register: stop_tomcat_logrotate
changed_when: false
- name: Stop Tomcat Logrotate Result
debug: var=stop_tomcat_logrotate.stdout_lines
- name: Change Logrotate
shell: >
sed -i /etc/logrotate.d/tomcat -e 's/rotate 52/rotate 30/';
sed -i /etc/logrotate.d/tomcat -e 's/weekly/daily/';
cat /etc/logrotate.d/tomcat;
register: change_logrotate
changed_when: false
- name: Change Logrotate Result
debug: var=change_logrotate.stdout_lines
- name: Deploy httpd Logrotate
template:
src: httpd.j2
dest: /etc/logrotate.d/httpd
mode: "0644"
owner: "root"
group: "root"
- name: Tomcat Start and AutoRun
service: name=tomcat state=started enabled=yes
- name: Apache Start and AutoRun
service: name=httpd state=started enabled=yes
- name: Mariadb Start and AutoRun
service: name=mariadb state=started enabled=yes
内容はApacheやTomcatの設定ファイルの配置や変更をして、各サービスを起動しています。
次にshibboleth-idp/setup-mariadbのロール処理です。
- name: Deploy SQL
template:
src: "{{ item }}"
dest: "{{ ansible_work }}/"
mode: "0644"
owner: root
group: root
with_items:
- add_priv.sql
- add_db.sql
- SService_table.sql
- SId_table.sql
- name: Execute SQL
shell: >
mysql < {{ ansible_work }}/add_priv.sql;
mysql -u{{ mysql.admin_id }} -p{{ mysql.admin_password }} < {{ ansible_work }}/add_db.sql;
mysql StorageService -u{{ mysql.admin_id }} -p{{ mysql.admin_password }} < {{ ansible_work }}/StorageService_table.sql;
mysql StoredID -u{{ mysql.admin_id }} -p{{ mysql.admin_password }} < {{ ansible_work }}/StoredId_table.sql;
内容はMariaDBのデータベースやテーブル作成、また管理者ユーザーの設定です。
次にshibboleth-idp/setup-idpのロール処理です。
- name: Deploy build.xml
template:
src: build.xml.j2
dest: "{{workdir}}/{{product_version.shibboleth_idp}}/bin/build.xml"
backup: yes
mode: "0644"
owner: "root"
group: "root"
- name: Install Shibboleth IdP
shell: >
cd {{workdir}}/{{product_version.shibboleth_idp}}/bin;
export JAVA_HOME="/usr/lib/jvm/jre-1.8.0";
./install.sh \
-Didp.src.dir={{workdir}}/{{product_version.shibboleth_idp}} \
-Didp.target.dir=/opt/shibboleth-idp \
-Didp.host.name={{shibboleth.site.fqdn}} \
-Didp.scope={{domain}} \
-Didp.sealer.password={{ shibboleth.sealer.keyPassword }} \
-Didp.keystore.password={{ shibboleth.sealer.storePassword }} \
-Didp.noprompt=true;
- name: Shibboleth IdP WAR deploy
copy:
remote_src: true
src: "{{shibboleth.deploy_dir}}/war/idp.war"
dest: /usr/share/tomcat/webapps/idp.war
mode: "0644"
owner: "tomcat"
group: "tomcat"
- name: Shibboleth IdP Dir Permission
shell: >
chown -Rh tomcat:tomcat {{shibboleth.deploy_dir}};
- name: Shibboleth IdP Log Dir
shell: >
rmdir {{shibboleth.deploy_dir}}/logs;
mkdir /var/log/shibboleth;
chown tomcat:root /var/log/shibboleth;
chmod 770 /var/log/shibboleth;
ln -s /var/log/shibboleth {{shibboleth.deploy_dir}}/logs;
failed_when: not "'File exists' in res.stderr"
- name: Deploy Shibboleth Logrotate
template:
src: shibboleth.j2
dest: /etc/logrotate.d/shibboleth
mode: "0644"
owner: "root"
group: "root"
- name: Check Certificate
stat: path="{{shibboleth.deploy_dir}}/credentials/{{certificate.certfile}}"
register: idpcert_file
- name: Shibboleth IdP Metadata Certificate
shell: >
cd {{shibboleth.deploy_dir}}/credentials;
openssl req -new -x509 -out {{certificate.certfile}} -subj "{{certificate.subject}}" -days {{certificate.validdays}} -newkey rsa:2048 -keyout {{certificate.privatefile}} -nodes -sha512;
chmod 440 {{shibboleth.deploy_dir}}/credentials/{{certificate.certfile}};
chmod 440 {{shibboleth.deploy_dir}}/credentials/{{certificate.privatefile}};
chown root:tomcat {{shibboleth.deploy_dir}}/credentials/{{certificate.certfile}};
chown root:tomcat {{shibboleth.deploy_dir}}/credentials/{{certificate.privatefile}};
when: [ idpcert_file.stat.md5 is not defined,
node == "master" ]
- name: Download Certificate
synchronize:
mode: pull
src: "{{ item }}"
dest: ./files/
with_items:
- "{{shibboleth.deploy_dir}}/credentials/{{certificate.certfile}}"
when: [ idpcert_file.stat.md5 is not defined,
node == "master" ]
- name: Deploy Certificate to Slave
copy:
src: "{{ item }}"
dest: "{{shibboleth.deploy_dir}}/credentials/"
mode: "0440"
owner: root
group: tomcat
with_items:
- "{{certificate.certfile}}"
when: [ idpcert_file.stat.md5 is not defined,
node == "slave" ]
- name: Shibboleth IdP Metadata Certificate Cash
shell: cat {{shibboleth.deploy_dir}}/credentials/{{certificate.certfile}} | grep -v CERTIFICATE;
register: idp_cert_cash
changed_when: false
when: node == "master"
- name: Generate Master Password Hash Result
debug: var=idp_cert_cash.stdout_lines
when: node == "master"
- name: Deploy idp.properties
template:
src: idp.properties.j2
dest: "{{shibboleth.deploy_dir}}/conf/idp.properties"
backup: yes
mode: "0770"
owner: tomcat
group: tomcat
- name: Deploy ldap.properties
template:
src: ldap.properties.j2
dest: "{{shibboleth.deploy_dir}}/conf/ldap.properties"
backup: yes
mode: "0770"
owner: tomcat
group: tomcat
- name: Deploy global.xml
template:
src: global.xml.j2
dest: "{{shibboleth.deploy_dir}}/conf/global.xml"
mode: "0770"
owner: tomcat
group: tomcat
- name: Deploy attribute-resolver.xml
template:
src: attribute-resolver.xml.j2
dest: "{{shibboleth.deploy_dir}}/conf/attribute-resolver.xml"
mode: "0770"
owner: tomcat
group: tomcat
- name: Deploy Conf
copy:
src: "{{item}}"
dest: "{{shibboleth.deploy_dir}}/conf"
mode: "0770"
owner: tomcat
group: tomcat
with_fileglob:
- "files/conf/*.xml"
- name: Deploy Conf/Intercept
copy:
src: "{{item}}"
dest: "{{shibboleth.deploy_dir}}/conf/intercept"
mode: "0770"
owner: tomcat
group: tomcat
with_fileglob:
- "files/conf/intercept/*.xml"
- name: Deploy Medadata XML File
template:
src: "metadata/{{ item.value.filename }}-metadata.xml"
dest: "/opt/shibboleth-idp/metadata"
mode: "0640"
owner: tomcat
group: tomcat
with_dict: "{{ shibboleth.metadata.entity }}"
- name: Deploy Message
copy:
src: "{{item}}"
dest: "{{shibboleth.deploy_dir}}/system/messages"
mode: "0770"
owner: tomcat
group: tomcat
with_fileglob:
- "system/messages/*.properties"
- name: Tomcat Start and AutoRun
service: name=tomcat state=started enabled=yes
- name: Apache Start and AutoRun
service: name=httpd state=started enabled=yes
内容はbuild.xmlを置き換えてShibbolethセットアップの実行と、
各設定ファイルのデプロイや証明書の配置して、ミドルウェアを再起動しています。
この処理の冒頭のbuild.xmlですが、元々のSbibbolethパッケージ/bin/build.xmlを以下のように修正したものを配置しています。
※記載しているのは該当部分のみです。
(修正前)
<target name="getentityid" if="idp.ask.entityid" depends="checkproperties">
<TGT>getentityid</TGT>
<fail if="idp.noprompt">Input needed, silence demanded</fail>
<local name="entityid" />
<input message="SAML EntityID:" addproperty="entityid" defaultvalue="https://${idp.host.name}/idp/shibboleth" />
<property name="idp.uri.subject.alt.name" value="${entityid}" />
<echo file="${idp.merge.properties}" append="yes">
idp.entityID=${entityid}
</echo>
</target>
以下に修正しています。
(修正後)
<target name="getentityid" if="idp.ask.entityid" depends="checkproperties">
<TGT>getentityid</TGT>
<local name="entityid" />
<property name="idp.uri.subject.alt.name" value="${entityid}" />
<echo file="${idp.merge.properties}" append="yes">
idp.entityID=${entityid}
</echo>
</target>
対話式のセットアップでエラーになるために修正しています。
これでShibboleth IdPの構築手順が終わりです。
実際にやってみてはまったのは、Shibbolethのセットアップは対話式で実行するのですが、
これをコマンドで実行するのに必要なオプションを調べることでした。
また、必要なオプションだけではエラーとなり失敗してしまいました。
検索しても有効な情報がヒットせずに試行錯誤して成功しました。
おわりに
ここで記載したものは私が積み上げてきたAnsibleのやり方ですので、これがベストプラクティスというわけではありません。
皆さんがこれを参考にShibboleth IdPのAnsible作成の一助となれば幸いです。