この記事は、New Relic Advent Calendar 2021 4日目の記事です。
APNs 証明書の有効性チェック結果を New Relic に送信して監視するシェルスクリプトを書きました。
要件
- 環境変数は
.env
ファイルにまとめる - pem, pkcs#12 (p12) 両形式に対応する
- Revoke にも対応する
- 有効期限を過ぎていなくても無効になる場合があることを考慮する
- 以下の形式で Event API を送信する
[
{
"eventType":"APNsCertificateCheck",
"hostname":"ホスト名",
"certfile":"チェックする証明書ファイルのパス",
"valid":有効なら true,
"daysRemaining":残り日数(数値)、期限を過ぎるとマイナスになる,
"exitCode":curlコマンドのexit code,
"errorMessage":"curlコマンドエラー時のメッセージ"
}
]
スクリプト
というわけで書いたスクリプトはこちら。
# .env ファイル
# アカウントIDの部分を埋めること
NEWRELIC_EVENT_API=https://insights-collector.newrelic.com/v1/accounts/(アカウントID)/events
# ライセンスキー
NEWRELIC_LICENSE_KEY=
# 証明書ファイルパス (.pem または .p12)
CERT_FILE=/tmp/sample.pem
# .p12 の展開に必要なパスワード .pem やパス無しの場合は空のままで OK
PKCS12_PASSWORD=
# 以下は Ubuntu 20.04 の場合 CentOS 7 の場合は /etc/ssl/certs/ca-bundle.crt
CA_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
# HTTP2 対応 curl のパス
CURL=/usr/bin/curl-7.79.1
#!/bin/sh
ENV_DIR=`dirname $0`
. $ENV_DIR/.env
validate_certificate()
{
if [ $EXT = "pem" ]; then
CURL_OPT="--cert $CERT_FILE --cacert $CA_CERT_FILE"
elif [ $EXT = "p12" ]; then
CURL_OPT="--cert-type P12 --cert $CERT_FILE:$PKCS12_PASSWORD --cacert $CA_CERT_FILE"
else
echo "Error: Unknown extension."
exit 1
fi
CURL_RESULT=`$CURL -s -S https://api.development.push.apple.com $CURL_OPT 2>&1`
CURL_EXIT_CODE=$?
CURL_ERROR_MESSAGE=`echo $CURL_RESULT | sed -ne '/err/p'`
if [ $CURL_EXIT_CODE -eq 0 ]; then
echo "This certificate is valid."
VALID=true
else
echo "This certificate is invalid."
VALID=false
fi
}
calc_days_remaining()
{
if [ $EXT = "pem" ]; then
END_DATE=`openssl x509 -enddate -noout -in $CERT_FILE | cut -d= -f 2`
elif [ $EXT = "p12" ]; then
END_DATE=`openssl pkcs12 -password pass:$PKCS12_PASSWORD -in $CERT_FILE -nokeys | openssl x509 -noout -enddate | cut -d= -f 2`
else
echo "Error: Unknown extension."
exit 1
fi
DAYS_REMAINING=`expr \( \`date --date "$END_DATE" +%s\` - \`date +%s\` \) / 86400`
}
send_newrelic_event()
{
cat << EOS | gzip -c | $CURL -X POST --cacert $CA_CERT_FILE -H "Content-Type: application/json" -H "Api-Key: $NEWRELIC_LICENSE_KEY" -H "Content-Encoding: gzip" $NEWRELIC_EVENT_API --data-binary @-
[
{
"eventType":"APNsCertificateCheck",
"hostname":"`hostname`",
"certfile":"$CERT_FILE",
"valid":$VALID,
"daysRemaining":$DAYS_REMAINING,
"exitCode":$CURL_EXIT_CODE,
"errorMessage":"$CURL_ERROR_MESSAGE"
}
]
EOS
}
echo "SSL Certificate Check Start"
EXT=${CERT_FILE##*.}
validate_certificate
calc_days_remaining
send_newrelic_event
echo "SSL Certificate Check End"
使い方
APNs 証明書のあるサーバに .env
と check_apns_cert.sh
を同じディレクトリに置いて cron
などで定期実行します。.env
の設定は適宜変更して下さい。
実行ユーザは証明書が読めるユーザなら何でも良いです。
# /etc/crontab
# 毎時 01 分に実行
01 * * * * root /opt/check_apns_cert/check_apns_cert.sh
説明
スクリプトを書く上でちょっと考えたポイントを説明します。
validate_certificate
Apple のサーバに接続してみて証明書の有効性を確認する関数です。
実際に接続してみることで有効期限切れだけでなく Revoke された場合のエラーも確認できます。curl
の出力は err
を含む文字のみ抽出しています。もし有効期限が切れていた場合は以下のようなエラーとなります。
curl: (56) OpenSSL SSL_read: error:14094415:SSL routines:ssl3_read_bytes:sslv3 alert certificate expired, errno 0
証明書のチェックには curl
を使います。APNs は HTTP2 対応が必須ですから HTTP2 に対応した curl を使います。(バージョン 7.43 以降)
例えば CentOS 7 の場合、標準の curl
では対応していません。別の curl
を使いましょう。
# HTTP2 に対応した curl のダウンロード
curl -o curl-7.79.1 -L https://github.com/moparisthebest/static-curl/releases/download/v7.79.1/curl-amd64
chmod +x curl-7.79.1
実際に接続してのチェックは openssl s_client ...
をコマンドを使うこともできるのですが、以下の理由から curl
を使っています。
- openssl 1.0.2 と 1.1.1 で出力の挙動が異なる
- 1.0.2 では接続後必ず入力を待つが、1.1.1 ではエラーがあると即切断される
- 証明書が PEM または DER 形式でないと NG
- 変換の工夫が必要になってしまう
-
curl
の場合は PKCS#12 も指定できる
calc_days_remaining
有効期限の残り日数を計算する関数です。
有効期限の終わりの日は openssl x509 -enddate
で取得できます。これは以下のような形式です。notAfter=
以降の文字列はそのまま date
に食わせることができます。
notAfter=Nov 28 23:59:59 2022 GMT
pkcs#12 ではこのような出力を得るコマンドがないため pem (x509) 形式に変換するコマンドをパイプしています。
send_newrelic_event
New Relic Event API で結果をイベントとして送信する関数です。
Event API のドキュメントはこちらです。
Introduction to the Event API | New Relic Documentation
このスクリプトで送信した内容は以下の NRQL で確認できます。
FROM APNsCertificateCheck SELECT *
New Relic Alert で監視
Alert policy では以下 2 つの NRQL で監視するようにしました。hostname
での絞り込みは必要に応じて変えてください。
-
valid
が false の場合(=証明書の有効性が切れた場合)は critical アラートを出す
FROM APNsCertificateCheck SELECT count(*) WHERE valid IS FALSE AND hostname = '特定のホスト名'
-
daysRemaining
が閾値を下回った場合(=証明書の有効期限が近づいてきた場合)は critical / warning アラートを出す
FROM APNsCertificateCheck SELECT latest(daysRemaining) WHERE hostname = '特定のホスト名'
補足:普通の SSL 証明書監視の場合
HTTPS で使う普通の SSL 証明書を監視したい場合は New Relic Synthetics の SSL 証明書監視を使いましょう。ポチポチするだけで簡単に SSL 証明書を監視できます。