[AWS] [OpenShift] Amazon SES のSMTP認証情報を更新し, AlertManager の通知エンドポイント情報へ反映させる

## ドキュメントの作成動機

きっかけは業務中のスクリプト作成からだが、

- AWS, OpenShift の各環境への CLI ツールを通したオペレーション
- 各オペレーションを継続実施することになったため, 一般化可能な処理をスクリプト化
- json, yaml 等の各出力の整形 および データファイルとしての出力処理

など, 複合的に Linux(Bash)/AWS/OpenShift への処理を実施する機会が得られたため, 後学のために
記事として残すことにした

## 目的

本記事で紹介するスクリプトでは以下を一括実施することを目的とする

- 指定されたIAM User(**Amazon SES のSMTPユーザーを想定**)の持つ認証情報を新規作成
  - IAM 用 **アクセスキー, シークレットキーの1組**
- 作成したシークレットキーから, **Amazon SES SMTP 用認証情報 (SMTPパスワード) を生成**
  - IAMアクセスキーは **SMTP認証情報の SMTPユーザー名と同値** のため, そのまま利用
- エンドポイント利用しているサービスのAmazon SES SMTP情報を更新
  - 本記事ではOpenShift環境のアラート通知機能である [AlertManager](https://github.com/prometheus/alertmanager) の持つ認証情報を更新
- 更新前まで利用していた古いIAM認証情報を削除


## 環境想定

対象環境の想定はざっくり以下の通り

| 名称 | 詳細 |
| ---- | ---- |
| インフラ環境 | AWS |
| コンテナ環境 | OpenShift Container Platform (v4.8 以降) |
| スクリプト実行環境 | RedHat Enterprise Linux 8以降 |
| AWS CLI | v2.4.21 以降 |
| Python | v3.6.3 以降 |
| `seskey.py` | 参照元 : [既存の Amazon SES SMTP IAM ユーザーのアクセスキーを更新するにはどうすればよいですか?](https://aws.amazon.com/jp/premiumsupport/knowledge-center/ses-rotate-smtp-access-keys/) |

その他設定は以下の通り

- スクリプト実行環境は以下の状態であるものとする
  - AWS CLIに必要な権限を付与済
  - `oc` コマンド で OpenShift クラスターへアクセス可能
- AWS CLI で利用するIAM User 情報は [Amazon SES 用SMTP認証情報 (参考: リンク先)](https://docs.aws.amazon.com/ja_jp/ses/latest/dg/send-email-smtp.html) とする
- OpenShift では [AlertManager(参考: リンク先)](https://docs.openshift.com/container-platform/4.8/monitoring/managing-alerts.html) 設定済

## 成果物

おもむろに作成したスクリプトを提示. 以下の通り.

```bash:update_ses_smtp_creds.sh
#!/bin/bash

set -eux
cd "$(dirname "$0")"

#
# PATH : AWS CLI のインストールパス /usr/local/bin 追加
#
export PATH="${PATH}:/usr/local/bin/"

# AWSリージョン名
REGION="ap-northeast-1"

#
# NAMESPACE : alertmanager リソースを含むproject名
# NEW_IAM_CREDS : AWS CLI で生成する認証キー出力先
# ALERTMANAGER_DATA : alertmanager 用secret が持つSMTP情報出力先
#
NAMESPACE="openshift-monitoring"
NEW_IAM_CREDS="/tmp/new_creds.json"
ALERTMANAGER_DATA="/tmp/alertmanager.yaml"

#
# IAM User の存在確認
# あれば 0, なければエラーコード(0以外)を返す
#
function check_iam_user_exists() {
  local username=$1

  aws iam get-user --user-name "${username}"
  return $?
}

#
# SES SMTP 認証キー更新用に IAM User認証情報を作成
# 保存先に NEW_IAM_CREDS を指定しjson (アクセスキー, シークレットキー)出力
#
function create_new_accesskey() {
  local username=$1

  aws iam create-access-key \
  --user-name ${username} | jq -r '.AccessKey | {accessKey: .AccessKeyId, secretKey: .SecretAccessKey}' | tee -a ${NEW_IAM_CREDS}
}

#
# SMTP認証キー更新後, alertmanager 用 secret へ新しい認証情報を適用
# 適用後, 古いAWS認証キーを削除
#
function update_alertmanager_data() {
  local username=$1

  # alartmanager 用 secret から 認証キーを含む '.data' 部をbase64デコードし, yamlファイルとして保存 
  oc get secret alertmanager-main \
  --template='{{ index .data "alertmanager.yaml" }}' | base64 -d | tee -a ${ALERTMANAGER_DATA}

  # alartmanager 用SMTP認証情報を読み込み, 更新前のふるいSMTP認証キーとして保存
  local OLD_ACCESS_KEY=$(cat "${ALERTMANAGER_DATA}" | grep smtp_auth_username | awk '{print $2}')

  # 作成済の新しいAWS認証キーからSMTP認証情報を生成
  # 
  # 認証キーの仕様として アクセスキーはIAM/SMTP で共通, シークレットキーのみIAM/SMTPで異なる
  # ため, 別途SMTP用シークレットキー生成用にpython スクリプトを利用する
  local NEW_SECRET_KEY=$(cat "${NEW_IAM_CREDS}" | jq -r '.secretKey')
  local NEW_SMTP_USER=$(cat "${NEW_IAM_CREDS}" | jq -r '.accessKey')
  local NEW_SMTP_PASSWD=$(seskey.py "${NEW_SECRET_KEY}" "${REGION}")

  # alertmanager 用 data yaml に新しいSMTP認証情報を sed による置換で埋め込み
  # 本処理中 sed のセパレータは '/' ではなく '|' としている
  sed -i "s|smtp_auth_username: .*|smtp_auth_username: ${NEW_SMTP_USER}|" ${ALERTMANAGER_DATA}
  sed -i "s|smtp_auth_password: .*|smtp_auth_password: ${NEW_SMTP_PASSWD}|" ${ALERTMANAGER_DATA}

  # 更新した alertmanager 用 data yaml を secret へ埋め込み, secret の 認証情報更新
  oc create secret generic alertmanager-main \
  --from-file=${ALERTMANAGER_DATA} --dry-run=client -o=yaml | oc replace secret --filename=-

  # 古い IAM User用認証キーを削除
  aws iam delete-access-key --user-name ${username} --access-key-id ${OLD_ACCESS_KEY}

  # /tmp で展開していたjson, yaml の各ファイルを削除
  rm -f ${NEW_IAM_CREDS} ${ALERTMANAGER_DATA}
}

# スクリプト実行時の引数ヘルプ表示を実施
# -u, -h の各オプションを想定
function usage() {
  cat <<EOF
Usage:
  $0 [-u iam_name] arg1 ...

Description:
  This script updates SMTP IAM user's credential key.

Options:
  -u (string)   The name of IAM Username
  -h            Display help.

EOF
  exit 1
}

#
# スクリプトの引数から ユーザ名を受け取り
# USER_ID ... ユーザー名
#
if [ "$OPTIND" = 1 ]; then
  while getopts u:h OPT
  do
    case $OPT in
      u)
        # ユーザー名の文字列受取
        USER_ID_STR=$OPTARG
        ;;
      h)
        usage
        ;;
      *)
        echo "Try to enter the h option." 1>&2
        ;;
    esac
  done
else
  echo "No installed getopts-command." 1>&2
  exit 1
fi
readonly USER_ID="${USER_ID_STR:-}"

#
# スクリプト実行時の引数からIAM User 名を受け取り、同名のIAMがなければ エラー終了
# IAM User が存在した場合, function 呼び出しで以下を実施
# 
# 1. 指定されたIAM User の持つ認証キーを新規作成
# 2. alertmanager のもつ Amazon SES SMTP情報を更新, 古い認証キー情報を削除
#
if check_iam_user_exists "${USER_ID}";  then
  oc login -u system:admin && oc project ${NAMESPACE}

  create_new_accesskey "${USER_ID}"
  update_alertmanager_data "${USER_ID}"
else
  echo "There's no such name's user. quit."
  exit 1
fi
```

## 利用方法

`seskey.py` と同階層のフォルダで以下のように実行

```prompt
$ bash ./update_ses_smtp_creds.sh -u <IAM User名>
```

## 参考文献

- [Bash Scriptの作法 - Qiita](https://qiita.com/autotaker1984/items/bc758fcf368c1a167353)
- [シェルスクリプトでハマったところ　- Qiita](https://qiita.com/pocket8137/items/db1d4c711aa36db12b56)
- [既存の Amazon SES SMTP IAM ユーザーのアクセスキーを更新するにはどうすればよいですか? - Amazon Web Service](https://aws.amazon.com/jp/premiumsupport/knowledge-center/ses-rotate-smtp-access-keys/)