Humanity

Edit the world by your favorite way

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

スクリプト

趣あふれるスクリプトですが、良かったらどうぞ。

gist.github.com

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 でも述語を定義する方法

stackoverflow.com

['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的Prologプログラミング

演算子

Prolog演算子を定義したり term_expansion を駆使すれば任意の言語をほぼそのまま評価できそうな気がする。 と思ったけどカンマ等はどうにもならないっぽい。

誰でも動かせるPrologで操作的意味論を書こうぜ

Prolog の op/3 の定義の仕方と、以下の記事の演算子の優先順位の定義の仕方が似てる。 Prolog 処理系もこんな感じでパースしてるんだろうか。 二項演算子以外は Prolog で定義できるのか知らないけど(三項演算子とか前置・後置演算子)。

<Insert Language Name Here> - How to make interesting little languages.

と思って調べたらできるっぽい(前置・後置の単項演算子を定義)。 三項演算子二項演算子の組み合わせだから cond ? expr1 : expr2 では ?:演算子を定義すればいいはず?

Prolog 写経記 その 88 if/1 - 日記

演算子定義のエラー回避

演算子を定義しようとしたけどできない、って時は op(400,xfx,'||'). みたいにシングルクォートで囲ってやれば演算子定義の際のエラーを回避できるかもしれない (よくわかってない)。

Google グループ

または :- multifile (=>)/2. みたいにすれば演算子をエクスポートする際のエラーを回避できるかもしれない (よくわかってない)。

stackoverflow.com

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+1Two2 ではなく 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

ツイ