Let's Encrypt の証明書の有効期限をチェックする Cron 用シェルスクリプト
まぁ certbot-auto renew
を定期的に実行すればいいんですが、チェックだけしたいって場合のスクリプト。
Cron で毎日実行する事を想定してます。
追記:切れる前メール通知来るの知りませんでした…
具体例
REPORT_DAYS_AGO
はデフォルトで 14 が指定されていますが、実行時に指定した場合、その日数でチェックします。
以下は REPORT_DAYS_AGO=20
として実行した場合の出力です。
example.com の証明書が報告日 (=有効期限の終了日 - REPORT_DAYS_AGO) を迎えているので終了コードは 1 です。
$ sudo REPORT_DAYS_AGO=20 ./check-le-date.sh ========== example.com ========== 有効期限が 2018/09/29 で切れます (残り 20 日) $ echo $? 1
以下は REPORT_DAYS_AGO=19 として実行した場合の出力です。 まだ報告日を迎えていないので出力はなしで、終了コードは 0 です。
$ sudo REPORT_DAYS_AGO=19 ./check-le-date.sh $ echo $? 0
スクリプト
趣あふれるスクリプトですが、良かったらどうぞ。
certbot-auto で ImportError: No module named cryptography.hazmat.bindings.openssl.binding
wget https://dl.eff.org/certbot-auto
とかしてダウンロードした certbot-auto スクリプトを実行しようとすると、
ImportError: No module named cryptography.hazmat.bindings.openssl.binding
のようなエラーが出る時がある。
詳細なエラー
$ ./certbot-auto --debug Requesting to rerun ./certbot-auto with root privileges... Bootstrapping dependencies for Amazon... (you can skip this with --no-bootstrap) yum is /usr/bin/yum yum is hashed (/usr/bin/yum) Loaded plugins: priorities, update-motd, upgrade-helper amzn-main | 2.1 kB 00:00:00 amzn-updates | 2.5 kB 00:00:00 1057 packages excluded due to repository priority protections Package gcc-4.8.5-1.22.amzn1.noarch already installed and latest version Package augeas-libs-1.0.0-5.7.amzn1.x86_64 already installed and latest version Package 1:openssl-1.0.2k-12.110.amzn1.x86_64 already installed and latest version Package 1:openssl-devel-1.0.2k-12.110.amzn1.x86_64 already installed and latest version Package libffi-devel-3.0.13-16.5.amzn1.x86_64 already installed and latest version Package system-rpm-config-9.0.3-42.28.amzn1.noarch already installed and latest version Package ca-certificates-2017.2.14-65.0.1.17.amzn1.noarch already installed and latest version Package python27-devel-2.7.14-1.123.amzn1.x86_64 already installed and latest version Package python27-virtualenv-15.1.0-1.14.amzn1.noarch already installed and latest version Package python27-tools-2.7.14-1.123.amzn1.x86_64 already installed and latest version Package python27-pip-9.0.3-1.26.amzn1.noarch already installed and latest version Package 1:mod_ssl-2.2.34-1.16.amzn1.x86_64 already installed and latest version Nothing to do Creating virtual environment... Installing Python packages... Installation succeeded. Traceback (most recent call last): File "/opt/eff.org/certbot/venv/bin/letsencrypt", line 7, in <module> from certbot.main import main File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/certbot/main.py", line 10, in <module> import josepy as jose File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/josepy/__init__.py", line 44, in <module> from josepy.interfaces import JSONDeSerializable File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/josepy/interfaces.py", line 8, in <module> from josepy import errors, util File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/josepy/util.py", line 4, in <module> import OpenSSL File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/OpenSSL/__init__.py", line 8, in <module> from OpenSSL import rand, crypto, SSL File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/OpenSSL/rand.py", line 12, in <module> from OpenSSL._util import ( File "/opt/eff.org/certbot/venv/local/lib/python2.7/dist-packages/OpenSSL/_util.py", line 6, in <module> from cryptography.hazmat.bindings.openssl.binding import Binding ImportError: No module named cryptography.hazmat.bindings.openssl.binding
certbot-auto
は初回実行時に /opt/eff.org/certbot/
以下にライブラリをインストールしようとするっぽい(なので mv /opt/eff.org/certbot{,.bak}
とかするとまた最初から依存ライブラリをダウンロードしようとする)が、
理由は不明だがなぜか一部のライブラリ (cryptography) がインストールされない時がある。
なので手動でインストールしてやると無事動くようになる。
(念のため事前に sudo 無しで動かして、 /opt/eff.org/certbot/ 以下にインストールされることを確認した)
$ sudo /opt/eff.org/certbot/venv/bin/pip install cryptography
インストール中に赤文字で以下のエラー(警告?)が出たので、念のため zope というライブラリもインストールする。
certbot 0.26.1 requires zope.interface, which is not installed. certbot-nginx 0.26.1 requires zope.interface, which is not installed.
$ sudo /opt/eff.org/certbot/venv/bin/pip install zope
Prolog に関するメモ
REPL でも述語を定義する方法
['user']
で REPL でも述語が定義できる。
| ?- ['user']. woman(mia). ^D
部分適用
Prolog で部分適用。
?- ['user']. add(X, Y, X+Y). |: true. ?- add(1, 2, R). R = 1+2. ?- call(add, 1, 2, R). R = 1+2. ?- call(add(1), 1, R). R = 1+1.
この記事で使われてて知った。
演算子
Prolog、演算子を定義したり term_expansion
を駆使すれば任意の言語をほぼそのまま評価できそうな気がする。
と思ったけどカンマ等はどうにもならないっぽい。
Prolog の op/3 の定義の仕方と、以下の記事の演算子の優先順位の定義の仕方が似てる。 Prolog 処理系もこんな感じでパースしてるんだろうか。 二項演算子以外は Prolog で定義できるのか知らないけど(三項演算子とか前置・後置演算子)。
<Insert Language Name Here> - How to make interesting little languages.
と思って調べたらできるっぽい(前置・後置の単項演算子を定義)。
三項演算子は二項演算子の組み合わせだから cond ? expr1 : expr2
では ?
と :
の演算子を定義すればいいはず?
演算子定義のエラー回避
演算子を定義しようとしたけどできない、って時は op(400,xfx,'||').
みたいにシングルクォートで囲ってやれば演算子定義の際のエラーを回避できるかもしれない (よくわかってない)。
または :- multifile (=>)/2.
みたいにすれば演算子をエクスポートする際のエラーを回避できるかもしれない (よくわかってない)。
Prolog 初学者向け解説
以下のページがよくまとまってて読みやすかった。 英語だけど分量が少ないし説明が簡潔。
A Concise Introduction To Prolog
あとこの言葉が好き。
Prolog is a notation for stating logical relations that happens to be executable.
というわけで以下上記のページで気になった項目をつまみ食い。
ジェネレーター
述語は複数の値を返せる。
Many predicates can be used as generators, to generate one solution after another until later conditions are met. For example,
?- member(X, [1, 2, 3, 4, 5]), X > 3. X = 4 ; X = 5.
succeeds and instantiates X to 4. If backed into, it re-instantiates X to 5. (But if you think declaratively, this just says "X is a member of the list [1, 2, 3, 4, 5] that is greater than 3.'')
例えば以下の記事では前のバージョンのコードでは find_tree という述語を作っていた。
% Path = [], Node = a++[b++[a++[]]] になる (複数個の回答は考えられてなかった…はず) find_tree(a++[b++[a++[]]], =(a++_), Path, Node).
でも Prolog ではそのような述語を作る必要が無く、単にツリーを traverse して各ノードを返すジェネレーターを作ればそれで事足りる。 例えば以下の様に(テストコードから引用)。
% a++_ にマッチするノードをリストアップ。ここでは以下のペアが返される。 % Path = [], Node = a++[b++[a++[]]] % Path = [0, 0], Node = a++[] traverse_tree(a++[b++[a++[]]], Path, Node), Node = a++_.
ジェネレーターのそれぞれの返り値をリストにしたければ findall/3
を使う。
1番目の引数には2番目の引数の式でキャプチャ (自分語録) したい変数を式の形で示すとその通りに3番目のリストに入る。
ここでは (Path, Node)
と指定しているので、
Results は [([], a++[b++[a++[]]]), ([0, 0], a++[])]
となる。
Node = a++_, findall((Path, Node), traverse_tree(a++[b++[a++[]]], Path, Node), Results).
Prolog は式をその場で評価しない。
例えば Two = 1+1
の Two
は 2
ではなく 1+1
になる。
ちなみに数値として評価したい場合は is
を使う (Two is 1+1
)。
そして (Path, Node)
はタプルみたいなもので、C 等で (1+2)*3
とする場合の優先順位を示す括弧とは全く違う (という認識)。
2つの変数の式の形を示せればいいので、2要素のリストとする事もできる。
Node = a++_, findall([Path, Node], traverse_tree(a++[b++[a++[]]], Path, Node), Results).
この例も分かりやすかった。
Simple Prolog generator - Stack Overflow
Cut-Fail
あ、これ再帰の深い所から抜け出して値を返したい時便利そう。 再帰の深い所で失敗した場合、再帰でちまちまバケツリレーしてたけど、cut-fail を使えばシンプルに書ける (はず)。
When one clause fails, the next one is tried. If you want the failure of a clause to cause the failure of the entire predicate, you can use a cut-fail combination:
sqrt(X, RootX) :- X < 0, !, fail. (more clauses of sqrt should follow)
This is a procedural shortcut that avoids the necessity of having X <= 0 in every clause; it is justified only if the test is complex and there are many clauses.
動的に述語を定義
動的に述語が定義できるらしい。 ただそれが良い書き方かどうかは分からない。 イミュータブルじゃなくなる気がするので、個人的には避けた方が良いような気もする?
Database manipulation predicates
assert(X)Add X to the database. For syntactic reasons, if X is not a base
ツイ
Prolog、ツリー構造のライブラリ書いてて一番楽しいって思ったの、ノードのパスを指定するとそれをノードと全体の式に分離する述語を思いついた時だった。
— tyru🍆 (@_tyru_) 2018年9月7日
split_tree(+Tree, +Path, -Node, -Expr :: -Node).
で、こんな感じになる
split_tree(a++[b++[]], [0], b++[], a++[Node] :: Node).
これを使えばノードの取得は単に Node を取って他は捨てればいいし、更新は Expr :: Node の Node を更新 (と言っていいのか分からんけど) すれば全体の Expr が求まるし、何より変数を含む式を返してもいいって事に気付けたのが一番の収穫だったと思う。
— tyru🍆 (@_tyru_) 2018年9月7日
それまでは callback 渡してツリーとノード渡して返す値を決めさせようか…とか手続き的に考えてたんだけど、全体とノードを抜き出した「式」を返せば呼出し元でどうにでもできるじゃんっていう気付き。
— tyru🍆 (@_tyru_) 2018年9月7日