【Golang,クロスコンパイル】SSL証明書の有効期限をコマンドで確認できるようにする!

2月 12, 2022

どもです。そろそろ新しい時計が欲しい、saisaiです。


今回はSSL証明書の有効期限をコマンドで手軽に確認できるようにしてみたいと思います。


みなさん、WebサイトなどのSSL/TLS化は行っておりますでしょうか。SSL証明書の有効期限切れを防ぐためにちょくちょくブラウザから期限をチェックしたり、管理台帳を作成したりしていることと思います。


無料の証明書をお使いの方は自動更新するようなスクリプトを作成してcronを仕込んだりしているのかなと思います。それでも、スクリプトがうまく起動せず証明書が更新されていないまま1ヶ月ほど放置してしまっていたということはありませんか。僕はあります…。


ブラウザから一々確認しなくても、Macのターミナルやサーバ内でコマンド一つで証明書の有効期限を確認できるようにしたいと思ったので、頑張って実装してみました。


今回目指すもののイメージとしては以下のような感じです。

saisai $ コマンドポチー
ドメインを入力してください:ドメインを入力する
->証明書の有効期限は(xxxx/xx/xx xx:xx)です。
->証明書の有効期限が切れています、更新してください!


有効期限が切れていなければその有効期限を、切れていれば警告を出力するようにしたいと思います。それでは、早速やっていきましょう!

SSL証明書の有効期限を取得するコードの作成

それでは早速やっていきましょう。まずはブログのSSL証明書有効期限を確認するコードを書いてみようと思います。今回のコードは以下の記事を参考にさせていただきました。

Golang の http コンポーネントを利用して証明書の有効期限日を取得する #golang

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
  //該当URLにGETし情報を取得、変数respに代入する。
	resp, _ := http.Get("https://graff-it-i.com/")

 //SSL証明書の有効期限をチェックし変数に代入する。
	expireUTCTime := resp.TLS.PeerCertificates[0].NotAfter
 //有効期限を日本時間に変更してさらに変数に代入する。
	expireJSTTime := expireUTCTime.In(time.FixedZone("Asia/Tokyo", 9*60*60))
	//出力フォーマットを変更し変数expireDateに代入する。
  expireDate := expireJSTTime.Format("2006/01/02 15:04")
 //現在時刻を上記と同様のフォーマットで変数todayに代入する。
	today := time.Now().Format("2006/01/02 15:04")

	if today < expireDate {
		fmt.Printf("証明書の有効期限は(%v)です。\n", expireDate)
	} else {
		fmt.Println("証明書が有効期限が切れています、更新してください!")
	}
}

実行すると以下のように出力してくれます。有効期限のステータスによって出力内容が判断され流ようになっています。

saisai % go run main.go
証明書の有効期限は(2021/03/08 22:32)です。

これでローカルマシン上で当ブログのSSL証明書の有効期限を確認することができました。

Goファイルをクロスコンパイルする

次はこのファイルをこのブログが置いてあるlightsail上で実行できるようにクロスコンパイルしていきましょう。まずはサーバのアーキテクチャを確認します。

ec2-user $ arch
x86_64

次にGoファイルをクロスコンパイルしていきます。今回は上記のアーキテクチャ情報とLinuxOSを指定するという2要素から以下のコマンドでコンパイルしています。皆さんは環境に応じてコマンド内容を変更していただければと思います。

saisai % GOOS=linux GOARCH=amd64 go build -o check-cert main.go

これでLinux(x86_64)上で実行可能なバイナリファイル(ファイル名check-cert)が作成されました。

サーバ内に転送し、動作を確認

では、作成したバイナリファイルをlightsail上のサーバに転送して動作を確認してみましょう。"scp"コマンドを利用してファイルを転送します。

saisai % sudo scp -i 鍵ファイルのパス バイナリファイルが存在するパス ユーザ名@グローバルIP:転送先パス

これで実装したいサーバにバイナリファイルを送ることができました。では、転送したディレクトリまで移動し、バイナリファイルを実行してみましょう。

ec2-user $ cd 転送先パス
ec2-user $ ./check-cert
証明書の有効期限は(2021/03/08 22:32)です。

無事、SSL証明書の有効期限を取得することができました!これでサーバ作業しているついでにそのサーバで運用しているサービスのSSL証明書有効期限を確認できます。

実行をさらにお手軽に

しかし、このままでは期限を確認するために毎回ディレクトリを移動してバイナリファイルを実行しなくてはなりません。それはさすがに面倒なので"alias"コマンドを使用してもっと簡単に実行できるようにしましょう。

ec2-user $ alias check-cert='(cd 転送先パス && ./cert-check)'
ec2-user $ check-cert
証明書の有効期限は(2021/03/08 22:32)です。

これで"check-cert"と入力するだけで証明書の有効期限が確認できます。aliasなのでタブ補完も効いて便利ですね!コマンド自体を()で囲っているのは、check-certを実行した時にカレントディレクトリを移動しないようにするためです。


設定したaliasはサーバ再起動などで消滅してしまうため、.bashrcに記載するなどしておくとより便利です!

ドメインを選択して証明書の期限をチェックしたい!

チェックするドメインごとにGoファイルを準備して、クロスコンパイルして…というのも面倒だな…。


もっと柔軟にできないだろうか、と思いさらに少し手を加えてみました。

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	fmt.Printf("ドメインを入力してください:")
	var a string
	fmt.Scan(&a) //インタラクティブにドメインを取得できる
	url := fmt.Sprintf("https://%v/", a) //特定のドメインを指定する必要がない
	resp, _ := http.Get(url)
	if err := recover(); err != nil {
		fmt.Printf("recoverd!\n")
	}

	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 {
		fmt.Printf("->証明書の有効期限は(%v)です。\n", expireDate)
	} else {
		fmt.Println("->証明書の有効期限が切れています、更新してください!")
	}
}

実行結果は以下の通りです。

saisai % go run main.go
ドメインを入力してください:amazon.com(入力する)
->証明書の有効期限は(2021/11/01 08:59)です。

これで入力したドメインに応じて証明書期限を出力してくれるようになりました!

まとめ

これで無事、SSL証明書の有効期限を確認することができました!


例えばログチェックをする際に、ついでに証明書がちゃんと更新されているか確認したりできます。わざわざブラウザから確認する必要はなくなりましたね!


コード自体を改修すれば、期限切れをSlackやLINEで通知できたりもします。可能性が広がりますね!


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


-saisai-


↓本日のオススメ教材

AWS,Golang

Posted by CY