Zabbix: Zabbixで複数のWebサービスのSSL期限を監視する

#!/usr/bin/env python2
# --------------------------------------------
# check_SSLexpiration_zabbix.py
# fork from : https://gist.github.com/ytyng/c844c985e4c0d96990e9
# thanks: http://qiita.com/uemura/items/a3a0937f77494e62213c
# --------------------------------------------

import sys
import re
import subprocess
import datetime

def parse_result_date_string(date_string):
    """
    :convert date string to datetime
    >>> date_string = "Feb  2 05:47:50 2015 GMT"
    >>> d = parse_result_date_string(date_string)
    >>> d
    datetime.datetime(2015, 2, 2, 5, 47, 50)
    """
    d = datetime.datetime.strptime(date_string, '%b %d %H:%M:%S %Y %Z')
    return d

re_start = re.compile(r'Not Before: (.+)')
re_end = re.compile(r'Not After : (.+)')

def get_date_strings_from_result(result):
    """
    :return: cert start date, cert expire date
    >>> text = "Not Before: Feb  2 05:47:47 2015 GMT\\n" \\
    ...        "Not After : Feb  4 16:03:29 2016 GMT"
    >>> get_date_strings_from_result(text)
    ('Feb  2 05:47:47 2015 GMT', 'Feb  4 16:03:29 2016 GMT')
    """

    rr_start = re_start.search(result)
    rr_end = re_end.search(result)

    return rr_start.group(1) if rr_start else None, \
           rr_end.group(1) if rr_end else None

def get_cert_start_expire_date(domain, port):
    """
    :return: cert start date, cert expire date
    """
    command = "openssl s_client -connect {}:{} -servername {} < /dev/null 2>/dev/null | openssl x509 -text 2>/dev/null | grep Not"

    command = command.format(domain,port,domain)
    out = subprocess.check_output(command, shell=True)
    ss, es = get_date_strings_from_result(out)
    return parse_result_date_string(ss), parse_result_date_string(es)

def get_cert_expire_delta(domain,port):
    """
    time delta cert expire date and now
    """
    start_date, expire_date = get_cert_start_expire_date(domain,port)
    return expire_date - datetime.datetime.now()

def main():

    try:
        domain = sys.argv[1]
        port   = sys.argv[2]
        delta = get_cert_expire_delta(domain,port)
        result_days = delta.days
    except:
        result_days = -1000

    print(result_days)

if __name__ == '__main__':
     main()
#!/bin/bash
# ----------------------------------------------
# configの中身をDisovery用のjsonぽく返すだけ
# thanks : https://qiita.com/usiusi360/items/51e8478cb080412958a4
# ----------------------------------------------

SCRIPTDIR="$(dirname $0)"
SCRIPTNAME="$(basename $0)"
CONFNAME="${SCRIPTDIR}/${SCRIPTNAME%\.*}.conf"


if [ ! -f "${CONFNAME}" ]; then
    echo "${CONFNAME}が存在しません"
    exit 1
fi

echo "{"
echo "  \"data\":["


FIRST=1
while read SERVERNAME PORT ; do
    if [ ${FIRST} -eq 1 ] ; then
        echo -n ""
        FIRST=0
    else
        echo ","
    fi

    echo -e -n "\t\t{ \"{#CRTCHK_TARGET_SERVERNAME}\": \"${SERVERNAME}\" , \"{#CRTCHK_TARGET_PORT}\": \"${PORT}\"}"
done < ${CONFNAME}

echo ""
echo "  ]"
echo "}"
UserParameter=crtchk.daystoexpire[*],/etc/zabbix/check_SSLexpiration_zabbix.py $1 $2
UserParameter=crtchk.discovery,/etc/zabbix/crtchk.discovery.sh
servername1 443
servername2 443
servername3 443
servername4 8443
servername5 443
servername6 8443
servername7 443


名前 SSL期限監視
タイプ Zabbixエージェント
キー crtchk.discovery
監視間隔 30m
存在しなくなったリソースの保持期間 3600
名前 crtchk-daystoexpire – {#CRTCHK_TARGET_SERVERNAME}:{#CRTCHK_TARGET_PORT}
タイプ Zabbixエージェント
キー crtchk.daystoexpire[{#CRTCHK_TARGET_SERVERNAME},{#CRTCHK_TARGET_PORT}]
データ型 数値(浮動小数)
単位 days
名前 SSL証明書の期限取得に失敗しました – {#CRTCHK_TARGET_SERVERNAME}:{#CRTCHK_TARGET_PORT}
条件式 crtchk.daystoexpire[{#CRTCHK_TARGET_SERVERNAME},{#CRTCHK_TARGET_PORT}].last()}=-1000
正常イベントの生成 条件式
障害イベント生成モード 単一
名前 SSL証明書の期限が近づいています – {#CRTCHK_TARGET_SERVERNAME}:{#CRTCHK_TARGET_PORT}
条件式 {Zabbix server:crtchk.daystoexpire[{#CRTCHK_TARGET_SERVERNAME},{#CRTCHK_TARGET_PORT}].last()}<10
正常イベントの生成 条件式
障害イベント生成モード 単一
依存関係 SSL証明書の期限取得に失敗しました – {#CRTCHK_TARGET_SERVERNAME}:{#CRTCHK_TARGET_PORT}
<?xml version="1.0" encoding="UTF-8"?>
<zabbix_export>
    <version>5.0</version>
    <date>2022-08-09T08:50:14Z</date>
    <groups>
        <group>
            <name>Templates</name>
        </group>
    </groups>
    <templates>
        <template>
            <template>Template Check SSL Certificate</template>
            <name>Template Check SSL Certificate</name>
            <groups>
                <group>
                    <name>Templates</name>
                </group>
            </groups>
            <applications>
                <application>
                    <name>SSL</name>
                </application>
            </applications>
            <discovery_rules>
                <discovery_rule>
                    <name>crtchk.discovery</name>
                    <key>crtchk.discovery</key>
                    <delay>30m</delay>
                    <lifetime>3600</lifetime>
                    <item_prototypes>
                        <item_prototype>
                            <name>crtchk-daystoexpire - {#CRTCHK_TARGET_SERVERNAME}:{#CRTCHK_TARGET_PORT}</name>
                            <key>crtchk.daystoexpire[{#CRTCHK_TARGET_SERVERNAME},{#CRTCHK_TARGET_PORT}]</key>
                            <delay>60m</delay>
                            <history>14d</history>
                            <trends>0</trends>
                            <units>days</units>
                            <applications>
                                <application>
                                    <name>SSL</name>
                                </application>
                            </applications>
                            <trigger_prototypes>
                                <trigger_prototype>
                                    <expression>{last()}=-1000</expression>
                                    <name>Failed to get SSL certificate's expiration date - {#CRTCHK_TARGET_SERVERNAME}:{#CRTCHK_TARGET_PORT}</name>
                                    <priority>HIGH</priority>
                                </trigger_prototype>
                                <trigger_prototype>
                                    <expression>{last()}&lt;14</expression>
                                    <name>The SSL certificate's expiration date is  is approaching. - {#CRTCHK_TARGET_SERVERNAME}:{#CRTCHK_TARGET_PORT}</name>
                                    <priority>HIGH</priority>
                                    <dependencies>
                                        <dependency>
                                            <name>Failed to get SSL certificate's expiration date - {#CRTCHK_TARGET_SERVERNAME}:{#CRTCHK_TARGET_PORT}</name>
                                            <expression>{Template Check SSL Certificate:crtchk.daystoexpire[{#CRTCHK_TARGET_SERVERNAME},{#CRTCHK_TARGET_PORT}].last()}=-1000</expression>
                                        </dependency>
                                    </dependencies>
                                </trigger_prototype>
                            </trigger_prototypes>
                        </item_prototype>
                    </item_prototypes>
                </discovery_rule>
            </discovery_rules>
        </template>
    </templates>
</zabbix_export>