【Golang】SSL証明書有効期限をLINEで通知してみた【LINEbot】

2月 12, 2022

どもども、だんだん気温が下がって過ごしやすくなってきましたね!スウェットやジャージを物色し始めたsaisaiです。


本日はこちらの記事で作成したSSL証明書の有効期限を確認するGoプログラムを応用して、結果をLINE通知できるようにしてみたいと思います。


SSL証明書の有効期限をGoで取得する方法については上記記事で事前に確認しておいてください!


では早速、SSL証明書有効期限をLINEで通知する方法を紹介したいと思います。

事前準備

まずはLINEbotを使用してGoプログラムからLINEメッセージを送信するための下準備をしておきます。


必要な準備は以下の通りです。
・LINE Developersのアカウント作成
・チャンネル新規作成
・CHANNEL_SECRET、CHANNEL_TOKEN、USER_IDの取得


LINEのAPIを使用するにはLINE Developersのアカウントを作成する必要があるので作成します。

その後メッセージを送信するチャンネルを作成してください。チャンネルに友達登録してもらうことでそのLINEユーザを対象にメッセージを送信することができます。

CHANNEL_SECRET、CHANNEL_TOKEN、USER_IDはプログラムから実際にLINEapiを叩くために必要になりますのでこちらも事前に取得しておきます。

実際の手順は本記事では省略させていただきますので、下記サイトを参照し準備を済ませておいてください!

Messaging APIを始めよう

準備ができましたら、実際にコードを書いていきます!

監視対象ドメインを取得する

さて、まずはSSL証明書有効期限を確認するドメイン(URL)を取得する方法についてです。


今回は複数ドメインを管理できるよう監視対象ドメイン一覧が記載されたファイルを別途作成し、プログラム側からそのファイルを読み込むようにしてみました。


まず監視対象ドメイン一覧ファイルを作成します。ここでは例として"domainlist.txt"という名前のファイルを作成し下記のようにドメインを記載してみます。

https://graff-it-i.com/
ドメイン2
ドメイン3
...

今回はテストのため本ブログのドメインのみを対象としますが、複数監視したい場合は一行ずつ監視対象URLを追加して行ってください。


ファイルの準備ができましたら、実際にプログラム側で読み込んでいきたいと思います。

func main() {
    filename := "domainlist.txt"

    fp, err := os.Open(filename)
    if err != nil {
    }
    defer fp.Close()

    scanner := bufio.NewScanner(fp)

    for scanner.Scan() {
        checkCert(scanner.Text())
    }

    if err = scanner.Err(); err != nil {
    }
}

“checkCert"はこの後紹介するSSL証明書有効期限を確認する関数です。ファイル"domainlist.txt"の内容を一行ずつ読み込み、結果を引数としてcheckCert関数を実行します。


これで複数ドメインを簡単に監視・管理することができるようになりました!

SSL証明書有効期限チェック

次にファイル"domainlist.txt"から読み込んだURLを対象にSSL証明書の有効期限をチェックします。


こちらは文頭で紹介した前回の記事とほとんど一緒ですので詳しい説明は省略します。

func checkCert( domain string ) string {
	
		resp, _ := http.Get(domain)

		expireUTCTime := resp.TLS.PeerCertificates[0].NotAfter
		expireJSTTime := expireUTCTime.In(time.FixedZone("Asia/Tokyo", 9*60*60))
		expireDate := expireJSTTime.Format("2006/01/02 15:04")
		today := time.Now().Format("2006/01/02 15:04")
	
		if today < expireDate {
			result := fmt.Sprintf("%s->証明書の有効期限は(%v)です。", domain, expireDate)
			postLineMessage(result)
		} else {
			result := fmt.Sprintf("%s->証明書の有効期限が切れています、更新してください!", domain)
			postLineMessage(result)
		}
		return "エラーが発生しています"
  }

前回までと少し違う部分は以下です。

if today < expireDate {
			result := fmt.Sprintf("%s->証明書の有効期限は(%v)です。", domain, expireDate)
			postLineMessage(result)
		} else {
			result := fmt.Sprintf("%s->証明書の有効期限が切れています、更新してください!", domain)
			postLineMessage(result)
		}

“postLineMessage"はこの後紹介するSSL証明書有効期限チェックの結果をLINEメッセージで通知する関数です。

前回は有効期限をチェックした結果をただ出力するだけでしたが、今回はその結果を引数としてpostLineMessage関数を実行するようにしています。

では、実際にLINE通知する処理を見ていきましょう。

結果をLINEで通知する

それではいよいよチェックの結果をLINE通知していきます。


まず最初に今回はLINEapiの認証情報を使用しますが、こちらは機微情報ですのでコードに直接書き込むのはあまり良くありません。gitignoreなどで特秘性を高めるためにも認証情報を別ファイルで定義し、プログラムから読み込むようにしてみましょう。


今回はgodotenvという非常に便利なものがありますのでこちらを使用していきたいと思います。詳しい説明は本記事では省略しますので気になった方は今回僕が参考にした下記記事を読んでみてください。

【Go】.envを使って環境変数を読み込む(godotenv) +osパッケージでenvを触ってみる。

godotenvを使用すると"環境変数を定義した別ファイルから変数の値を読み込む"ことができるようになります。


まず初めに必要となるパッケージをインストールしておきます。

github.com/line/line-bot-sdk-go/linebot
go get github.com/joho/godotenv



次に環境変数を定義するファイル(ここでは.envというファイル名とします)を作成し中に変数を定義していきましょう。

CHANNEL_SECRET= "チャンネルシークレット"
CHANNEL_TOKEN= "チャンネルトークン"
USER_ID= "ユーザID"

“"の中に事前に取得した値を入力してください。

それでは上記の認証情報を使用してLINEメッセージを送信してみましょう。

  func postLineMessage( result string ) {
	err := godotenv.Load(".env")
    bot, err := linebot.New(os.Getenv("CHANNEL_SECRET"), os.Getenv("CHANNEL_TOKEN"))
    if err != nil {
        fmt.Println(err)
    }
    if _, err := bot.PushMessage(os.Getenv("USER_ID"), linebot.NewTextMessage(result)).Do(); err != nil {
        fmt.Println(err)
    }
}

まず下記の部分でファイル内の変数を読み込み

err := godotenv.Load(".env")

読み込んだ認証情報を使用してbotを作成

bot, err := linebot.New(os.Getenv("CHANNEL_SECRET"), os.Getenv("CHANNEL_TOKEN"))

さらに読み込んだユーザIDを使用し、checkCert関数で取得した有効期限チェックの結果をLINEプッシュメッセージとして送信しています。

    if _, err := bot.PushMessage(os.Getenv("USER_ID"), linebot.NewTextMessage(result)).Do(); err != nil {
        fmt.Println(err)
    }

これで必要な一通りの処理を実装することができました。

コード全体

最後にコードの全体像をお見せします。domainlist.txtや.envはこちらのプログラムと同じディレクトリ に作成したおいてください。

package main

import (
	"fmt"
	"net/http"
	"time"
	"os"
	"bufio"

	"github.com/line/line-bot-sdk-go/linebot"
	"github.com/joho/godotenv"
)

func main() {
    filename := "domainlist.txt"

    fp, err := os.Open(filename)
    if err != nil {
    }
    defer fp.Close()

    scanner := bufio.NewScanner(fp)

    for scanner.Scan() {
        checkCert(scanner.Text())
    }

    if err = scanner.Err(); err != nil {
    }
}

func checkCert( domain string ) string {
	
		resp, _ := http.Get(domain)

		expireUTCTime := resp.TLS.PeerCertificates[0].NotAfter
		expireJSTTime := expireUTCTime.In(time.FixedZone("Asia/Tokyo", 9*60*60))
		expireDate := expireJSTTime.Format("2006/01/02 15:04")
		today := time.Now().Format("2006/01/02 15:04")
	
		if today < expireDate {
			result := fmt.Sprintf("%s->証明書の有効期限は(%v)です。", domain, expireDate)
			postLineMessage(result)
		} else {
			result := fmt.Sprintf("%s->証明書の有効期限が切れています、更新してください!", domain)
			postLineMessage(result)
		}
		return "エラーが発生しています"
  }

  func postLineMessage( result string ) {
	err := godotenv.Load(".env")
    bot, err := linebot.New(os.Getenv("CHANNEL_SECRET"), os.Getenv("CHANNEL_TOKEN"))
    if err != nil {
        fmt.Println(err)
    }
    if _, err := bot.PushMessage(os.Getenv("USER_ID"), linebot.NewTextMessage(result)).Do(); err != nil {
        fmt.Println(err)
    }
}

実際に実行してみましょう。

$go run main.go

このように証明書の有効期限(あるいは期限切れであること)がLINEメッセージで送られてきたら成功です!

アイコンは我が家のうさちゃんです。

まとめ

今回はSSL証明書の有効期限をLINE通知するプログラムを作成してみました。


一応完成はしましたが、ドメインの数だけ処理が走ったりメッセージがその分送られてきたりともう少しスマートにしたい点はあります。


実は最近ありがたいことに個人でHP作成・保守をお手伝いする仕事をいただくことがあり、HP監視の一環として使用したいシステムだったので作成してみようというのがきっかけでした。


あとはクロスコンパイルしてどこかのサーバでcronで回すか、Lambda化してCloudWatchEventで回すか検討して運用していこうと思います。


最後まで読んでいただきありがとうございました!


↓オススメ教材

Golang

Posted by CY