Ansibleのシェルスクリプト

顧客が、RHELサーバーとAIXサーバーに集中化されたsudoersファイルを展開するためのスクリプト移行の支援を求めたとします。







まあ、これは非常に一般的なシナリオです。その例では、Ansibleの高度な機能の使用方法と、アプローチがどのように変化するかを示すことができます-特定のタスクを実行するスクリプトから、dem等の(変更を加えずに)説明とインスタンスの状態の監視



スクリプトを実行します。



#!/bin/sh # Desc: Distribute unified copy of /etc/sudoers # # $Id: $ #set -x export ODMDIR=/etc/repos # # perform any cleanup actions we need to do, and then exit with the # passed status/return code # clean_exit() { cd / test -f "$tmpfile" && rm $tmpfile exit $1 } #Set variables PROG=`basename $0` PLAT=`uname -s|awk '{print $1}'` HOSTNAME=`uname -n | awk -F. '{print $1}'` HOSTPFX=$(echo $HOSTNAME |cut -c 1-2) NFSserver="nfs-server" NFSdir="/NFS/AIXSOFT_NFS" MOUNTPT="/mnt.$$" MAILTO="unix@company.com" DSTRING=$(date +%Y%m%d%H%M) LOGFILE="/tmp/${PROG}.dist_sudoers.${DSTRING}.log" BKUPFILE=/etc/sudoers.${DSTRING} SRCFILE=${MOUNTPT}/skel/sudoers-uni MD5FILE="/.sudoers.md5" echo "Starting ${PROG} on ${HOSTNAME}" >> ${LOGFILE} 2>&1 # Make sure we run as root runas=`id | awk -F'(' '{print $1}' | awk -F'=' '{print $2}'` if [ $runas -ne 0 ] ; then echo "$PROG: you must be root to run this script." >> ${LOGFILE} 2>&1 exit 1 fi case "$PLAT" in SunOS) export PINGP=" -t 7 $NFSserver " export MOUNTP=" -F nfs -o vers=3,soft " export PATH="/usr/sbin:/usr/bin" echo "SunOS" >> ${LOGFILE} 2>&1 exit 0 ;; AIX) export PINGP=" -T 7 $NFSserver 2 2" export MOUNTP=" -o vers=3,bsy,soft " export PATH="/usr/bin:/etc:/usr/sbin:/usr/ucb:/usr/bin/X11:/sbin:/usr/java5/jre/bin:/usr/java5/bin" printf "Continuing on AIX...\n\n" >> ${LOGFILE} 2>&1 ;; Linux) export PINGP=" -t 7 -c 2 $NFSserver" export MOUNTP=" -o nfsvers=3,soft " export PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin" printf "Continuing on Linux...\n\n" >> ${LOGFILE} 2>&1 ;; *) echo "Unsupported Platform." >> ${LOGFILE} 2>&1 exit 1 esac ## ## Exclude Lawson Hosts ## if [ ${HOSTPFX} = "la" ] then echo "Exiting Lawson host ${HOSTNAME} with no changes." >> ${LOGFILE} 2>&1 exit 0 fi ## ## * NFS Mount Section * ## ## Check to make sure NFS host is up printf "Current PATH is..." >> ${LOGFILE} 2>&1 echo $PATH >> $LOGFILE 2>&1 ping $PINGP >> $LOGFILE 2>&1 if [ $? -ne 0 ]; then echo " NFS server is DOWN ... ABORTING SCRIPT ... Please check server..." >> $LOGFILE echo "$PROG failed on $HOSTNAME ... NFS server is DOWN ... ABORTING SCRIPT ... Please check server ... " | mailx -s "$PROG Failed on $HOSTNAME" $MAILTO exit 1 else echo " NFS server is UP ... We will continue..." >> $LOGFILE fi ## ## Mount NFS share to HOSTNAME. We do this using a soft mount in case it is lost during a backup ## mkdir $MOUNTPT mount $MOUNTP $NFSserver:${NFSdir} $MOUNTPT >> $LOGFILE 2>&1 ## ## Check to make sure mount command returned 0. If it did not odds are something else is mounted on /mnt.$$ ## if [ $? -ne 0 ]; then echo " Mount command did not work ... Please check server ... Odds are something is mounted on $MOUNTPT ..." >> $LOGFILE echo " $PROG failed on $HOSTNAME ... Mount command did not work ... Please check server ... Odds are something is mounted on $MOUNTPT ..." | mailx -s "$PROG Failed on $HOSTNAME" $MAILTO exit 1 else echo " Mount command returned a good status which means $MOUNPT was free for us to use ... We will now continue ..." >> $LOGFILE fi ## ## Now check to see if the mount worked ## if [ ! -f ${SRCFILE} ]; then echo " File ${SRCFILE} is missing... Maybe NFS mount did NOT WORK ... Please check server ..." >> $LOGFILE echo " $PROG failed on $HOSTNAME ... File ${SRCFILE} is missing... Maybe NFS mount did NOT WORK ... Please check server ..." | mailx -s "$PROG Failed on $HOSTNAME" $MA ILTO umount -f $MOUNTPT >> $LOGFILE rmdir $MOUNTPT >> $LOGFILE exit 1 else echo " NFS mount worked we are going to continue ..." >> $LOGFILE fi ## ## * Main Section * ## if [ ! -f ${BKUPFILE} ] then cp -p /etc/sudoers ${BKUPFILE} else echo "Backup file already exists$" >> ${LOGFILE} 2>&1 exit 1 fi if [ -f "$SRCFILE" ] then echo "Copying in new sudoers file from $SRCFILE." >> ${LOGFILE} 2>&1 cp -p $SRCFILE /etc/sudoers chmod 440 /etc/sudoers else echo "Source file not found" >> ${LOGFILE} 2>&1 exit 1 fi echo >> ${LOGFILE} 2>&1 visudo -c |tee -a ${LOGFILE} if [ $? -ne 0 ] then echo "sudoers syntax error on $HOSTNAME." >> ${LOGFILE} 2>&1 mailx -s "${PROG}: sudoers syntax error on $HOSTNAME" "$MAILTO" << EOF Syntax error /etc/sudoers on $HOSTNAME. Reverting changes Please investigate. EOF echo "Reverting changes." >> ${LOGFILE} 2>&1 cp -p ${BKUPFILE} /etc/sudoers else # # Update checksum file # grep -v '/etc/sudoers' ${MD5FILE} > ${MD5FILE}.tmp csum /etc/sudoers >> ${MD5FILE}.tmp mv ${MD5FILE}.tmp ${MD5FILE} chmod 600 ${MD5FILE} fi echo >> ${LOGFILE} 2>&1 if [ "${HOSTPFX}" = "hd" ] then printf "\nAppending #includedir /etc/sudoers.d at end of file.\n" >> ${LOGFILE} 2>&1 echo "" >> /etc/sudoers echo "## Read drop-in files from /etc/sudoers.d (the # here does not mean a comment)" >> /etc/sudoers echo "#includedir /etc/sudoers.d" >> /etc/sudoers fi ## ## * NFS Un-mount Section * ## ## ## Unmount /mnt.$$ directory ## umount ${MOUNTPT} >> $LOGFILE 2>&1 if [ -d ${MOUNTPT} ]; then rmdir ${MOUNTPT} >> $LOGFILE 2>&1 fi ## ## Make sure that /mnt.$$ got unmounted ## if [ -f ${SRCFILE} ]; then echo " The umount command failed to unmount ${MOUNTPT} ... We will not force the unmount ..." >> $LOGFILE umount -f ${MOUNTPT} >> $LOGFILE 2>&1 if [ -d ${MOUNTPT} ]; then rmdir ${MOUNTPT} >> $LOGFILE 2>&1 fi else echo " $MOUNTPT was unmounted ... There is no need for user intervention on $HOSTNAME ..." >> $LOGFILE fi # # as always, exit cleanly # clean_exit 0
      
      





212行のコードがあり、sudoersファイルにはバージョン管理がありません。 顧客は、週に1回実行され、ファイルのチェックサムをチェックしてセキュリティを確保するプロセスをすでに持っています。 スクリプトにはSolarisへの参照が含まれていますが、この顧客のためにこの要件を転送する必要もありませんでした。



まず、ロールを作成し、バージョン管理のためにGitにsudoersファイルを配置します。 とりわけ、これにより、NFSボリュームをマウントする必要がなくなります。



コピーおよびテンプレートモジュールの検証およびバックアップオプションを使用すると、ファイルをバックアップおよび復元するコードを記述する必要がなくなります。 この場合、ファイルが宛先ポイントに配置される前に検証が実行され、検証が失敗した場合、モジュールはエラーをスローします。



ロールごとに、タスク、テンプレート、および変数を指定する必要があります。 対応するファイルの構造は次のとおりです。







ロールスクリプトファイル(プレイブック) sudoers.ymlの構造は単純です:



---

##

# Role playbook

##

- hosts: all

roles:

- sudoers

...







ロール変数はvars / main.ymlファイルにあります 。 このファイルにはチェックサムファイルが含まれ、ローソンホストをスキップしてhdホストのみにsudoers.dファイルを含めるための特別なロジックを作成するために使用されるinclude / excludeディレクティブが含まれます。



vars / main.ymlファイルの内容は次のとおりです



---

MD5FILE: /root/.sudoer.md5

EXCLUDE: la

INCLUDE: hd

...







copyおよびlineinfileモジュールを使用する場合、ロールはべき等ではありません。 コピーモジュールはベースファイルをインストールし、lineinfileは開始するたびにincludeを再挿入します。 この役割はAnsible Towerで開始されるため、べき等性は必須です。 ファイルをjinja2テンプレートに変換します。



最初の行に、 スペースとインデント制御する次のコマンドを追加します。



#jinja2: lstrip_blocks: True, trim_blocks: True







テンプレートモジュールの新しいバージョンには、 trim_blocks (Ansible 2.4で追加)のパラメーターが含まれていることに注意してください。



ファイルの最後にインクルード行を挿入するコードは次のとおりです。



{% if ansible_hostname[0:2] == INCLUDE %}

#includedir /etc/sudoers.d

{% endif %}







名前が文字「hd」で始まるホスト用の行を挿入するシェルコマンドに、条件付き構成({%if%}、{%endif%})を使用します。 Ansibleファクトとフィルター[0:2]を使用して、ホスト名を解析します。



それでは、タスクに移りましょう。 最初に、ホスト名を解析するためのファクトを確立する必要があります。 条件構成では、事実「parhost」を使用します。



---

##

# Parse hostnames to grab 1st 2 characters

##

- name: "Parse hostname's 1st 2 characters"

set_fact: parhost={{ ansible_hostname[0:2] }}







RHELストックサーバーにはcsumパラメーターはありません。 必要に応じて、別のファクトを使用して、チェックサムでバイナリファイルの名前を条件付きで示すことができます。 これらの機能がAIX、Solaris、およびLinuxで異なる場合、追加のコードが必要になる場合があることに注意してください。



さらに、AIXとRHELのルートグループの違いの問題はまだ解決されていません。



##

# Conditionally set name of checksum binary

##

- name: "set checksum binary"

set_fact:

csbin: "{{ 'cksum' if (ansible_distribution == 'RedHat') else 'csum' }}"



##

# Conditionally set name of root group

##

- name: "set system group"

set_fact:

sysgroup: "{{ 'root' if (ansible_distribution == 'RedHat') else 'sys' }}"







ブロックを使用すると、タスク全体の条件を設定できます。 ブロックの最後の条件を使用して、「la」ホストを除外します。



##

# Enclose in block so we can use parhost to exclude hosts

##

- block:







テンプレートモジュールは、ファイルを検証してインストールします。 タスクが変更されたかどうかを判断できるように、結果を修正します。 このモジュールでvalidateパラメーターを使用すると、ホストに配置する前に新しいsudoerファイルの有効性を検証できます。



##

# Validate will prevent bad files, no need to revert

# Jinja2 template will add include line

##

- name: Ensure sudoers file

template:

src: sudoers.j2

dest: /etc/sudoers

owner: root

group: "{{ sysgroup }}"

mode: 0440

backup: yes

validate: /usr/sbin/visudo -cf %s

register: sudochg







新しいテンプレートがインストールされている場合は、シェルスクリプトを実行してチェックサムファイルを生成します。 条件付きコンストラクトは、sudoersテンプレートのインストール時、またはチェックサムファイルが欠落している場合にチェックサムファイルを更新します。 実行中のプロセスは他のファイルも監視するため、ソーススクリプトで提供されるシェルコードを使用します。



- name: sudoers checksum

shell: "grep -v '/etc/sudoers' {{ MD5FILE }} > {{ MD5FILE }}.tmp ; {{ csbin }} /etc/sudoers >> {{ MD5FILE }} ; mv {{ MD5FILE }}.tmp {{ MD5FILE }}"

when: sudochg.changed or MD5STAT.exists == false







ファイルモジュールは、必要な権限のインストールを確認します。



- name: Ensure MD5FILE permissions

file:

path: "{{ MD5FILE }}"

owner: root

group: "{{ sysgroup }}"

mode: 0600

state: file







バックアップパラメータには、以前のバックアップを処理するためのオプションがないため、適切なコードを自分で作成する必要があります。 以下の例では、これに「register」パラメーターと「stdout_lines」フィールドを使用します。



##

# List and clean up backup files. Retain 3 copies.

##

- name: List /etc/sudoers.*~ files

shell: "ls -t /etc/sudoers*~ |tail -n +4"

register: LIST_SUDOERS

changed_when: false



- name: Cleanup /etc/sudoers.*~ files

file:

path: "{{ item }}"

state: absent

loop: "{{ LIST_SUDOERS.stdout_lines }}"

when: LIST_SUDOERS.stdout_lines != ""







ブロック完了:



##

# This conditional restricts what hosts this block runs on

##

when: parhost != EXCLUDE

...







意図されたユースケースは、Ansible Towerでこの役割を果たすことです。 Ansible Tower Alertsを設定して、ジョブの実行に失敗した場合、アラートを電子メール、Slack、またはその他に送信することができます。 このロールは、Ansible、Ansible Engine、またはAnsible Towerで実行されます。



その結果、スクリプトから不要なものをすべて削除し、sudoersファイルの目的の状態を提供できる完全にべき等の役割を作成しました。 SCMを使用すると、バージョン管理が可能になり、変更管理と透過性がより効率的になります。 Jenkinsまたは他のツールを備えたCI / CDを使用すると、将来の変更に備えてAnsibleコードの自動テストを設定できます。 Ansible TowerのAuditorロールを使用すると、組織の要件を監視および実施できます。



チェックサムを操作するためのコードはスクリプトから削除できますが、このためには、顧客はまずセキュリティサービスに相談する必要があります。 必要に応じて、sudoersパターンをAnsible Vaultで保護できます。 最後に、グループを使用すると、包含および除外を使用したロジックの記述が回避されます。



→このリンクで GitHubからロールをダウンロードできます。



All Articles