SSL証明書の有効期限を定期的にチェックし期限が近づいたら通知する

# はじめに

SSL証明書の有効期限が近付いた場合は警告メールを送ってくれるサービスもありますが、警告メールのタイトル・内容が英文になっていることで見逃してしまうことや、また届いたこと・期限が近いことは把握していてもうっかり有効期限切れ直前に急な割り込みにより失念してしまうこともあります。

そこで、、、

・メールとは違う何かで通知
・有効期限に近づいた時は通知頻度を短くして何度も通知

を実現したいと考えました。
幸い、既に以下のような仕組み

https://qiita.com/SECUAL_masa/items/a7a96499718bb4a9f0e2

があったのでそれを流用して、

>・メールとは違う何かで通知

　→　Slackで通知

>・有効期限に近づいた時は通知頻度を短くして何度も通知

　→　シェルスクリプトで30日、14日、7日、3日以内は毎日通知

を実現しようと思います。

# システム概要図

※今回の構成図もLucidchartを利用させてもらい作成しました。(無料枠)

![SSL証明書チェック構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1236186/7a4937d1-cc41-6ff7-50c1-72406662d300.png)

大まかなフローは以下の通りです。

1. 有効期限チェック
2. 通知
3. 確認
4. 更新

今回の記事では「有効期限チェック」の部分に焦点をあてて説明します。

# シェルスクリプト

概要図中の実行環境で動かすシェルスクリプトです。
SSL証明書の有効期限取得、チェック、Slack投稿を行います。

```Bash:check_cert.sh
#!/bin/bash

HOSTLIST=(
    "xxx.secual-inc.com:443"
)

WORKDIR="/public/toolz/check_cert"

RESULT_MSG=$WORKDIR"/check_cert_msg.txt"
RESULT_JSON=$WORKDIR"/check_cert.json"

LOCKFILE="/var/run/check_cert.lock"
if [ -f $LOCKFILE ]; then
    echo "Already exist lock file. ["$LOCKFILE"]"
    exit 9
fi
trap '{
    rm $LOCKFILE;
    exit $EXIT;
}' EXIT
touch $LOCKFILE

NOW=`date +%s`

EXIT=0

echo -n > $RESULT_MSG

for host in "${HOSTLIST[@]}"; do
    STREXPIREDATE=`openssl s_client -connect $host < /dev/null 2> /dev/null | openssl x509 -text | grep "Not After" | sed -e 's/.*Not After : //g'`
    if [ -z "$STREXPIREDATE" ]; then
        continue;
    fi

    EXPIREDATE=`date --date="$STREXPIREDATE" +%s`

    LAST=$(($((EXPIREDATE-NOW)) / 60 / 60/ 24))

    if [ $LAST -gt 30 ]; then
        continue;
    elif [ $LAST -lt 30 ] && [ $LAST -gt 14 ]; then
        continue;
    elif [ $LAST -lt 14 ] && [ $LAST -gt 7 ]; then
        continue;
    elif [ $LAST -lt 7 ] && [ $LAST -gt 4 ]; then
        continue;
    fi

    STREXPIREDATEJST=`date -d @$EXPIREDATE +"%Y/%m/%d %H:%M:%S"`

    if [ $LAST -gt 0 ]; then
        echo -n "△ ： " >> $RESULT_MSG
    else
        echo -n "× ： " >> $RESULT_MSG
    fi

    echo $host" : 有効期限["$STREXPIREDATEJST"]" >> $RESULT_MSG
done

if [ ! -s $RESULT_MSG ]; then
    EXIT=1
    exit
fi

###############################################################################
### post slack
###############################################################################

echo 'payload={"channel": "#service_checker", "username": "service checker bot", "text": "@here 証明書の有効期限通知です。 \n```' > $RESULT_JSON
cat $RESULT_MSG | sed -z 's/\n/\\n/g' >> $RESULT_JSON
echo '```", "icon_emoji": ":guardsman:", "link_names" : true}' >> $RESULT_JSON

curl -s -X POST -d @$RESULT_JSON https://hooks.slack.com/services/xxxxx/xxxxx/xxxxx

EXIT=0
exit
```

ロックファイルやtrapの話は、是非以下の記事を参照ください。

シェルスクリプト＋ロックファイルで二重実行防止 - Qiita
https://qiita.com/SECUAL_masa/items/5b1b7e8c4ed13c420435

それ以外については、個別に説明していきます。

## チェック対象ホストリスト

```Text:ホストリスト
HOSTLIST=(
    "xxx1.secual-inc.com:443"
    "xxx2.secual-inc.com:443"
)
```

SSL証明書を利用しているサーバー＆チェック対象のサーバーを列挙していきます。
"ホスト名:ポート番号"の形式になっています。これは次に記載する有効期限を取得するためのコマンドで利用するのがこの形式になっているためです。

## 有効期限取得

```Text:有効期限取得＋整形
    STREXPIREDATE=`openssl s_client -connect $host < /dev/null 2> /dev/null | openssl x509 -text | grep "Not After" | sed -e 's/.*Not After : //g'`

```

この部分が今回の肝になります。上記コマンドで有効期限を取得します。

```
openssl s_client -connect $host < /dev/null 2> /dev/null | openssl x509 -text
```

↑の部分でSSLの証明書の情報が取得でき、また"Not After"の部分が有効期限になりますので、それをgrep "Not After"で取り出し、また"sed -e 's/.*Not After : //g'"で後の日数算出に利用しやすい形式に整形します。

```Text:有効期限取得＋整形の結果($STREXPIREDATEに入る値)
Aug 04 20:24:54 2021 GMT
```

## 有効期限変換(日時 → 日数)

```Text:日数変換
    EXPIREDATE=`date --date="$STREXPIREDATE" +%s`

    LAST=$(($((EXPIREDATE-NOW)) / 60 / 60/ 24))
```

通知を
・30日
・14日
・7日
・3日以内
で実施しますので、その判定のための日数を取得するためいったん”Aug 04 20:24:54 2021 GMT"をUnixTimeに変換し、さらに日数に変換しています。

```Text:UnixTime($EXPIREDATEに入る値)
1628108694
```

```Text:UnixTime($LASTに入る値)
30
```

以降はその日数の判定と、Slackへ投稿するメッセージの構築、投稿になりますので省略します。


# 定期実行設定

あとはシェルスクリプトをcronで指定して定期的に実行するようにします。

```Text:cron
0 9 * * * /public/toolz/check_cert/check_cert.sh
```

# Slackへの通知

今回の仕組みで最終的に以下のようにSlackに投稿されます。

![投稿結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1236186/2df6ec35-ebae-86bd-044a-f6744fd58529.png)