どちらの式も水曜日の朝に見えるが、一回だけを意味するのは片方だけである。0 10 * * 3 は水曜日の 10:00 に発火し、* 10 * * 3 は十時台の毎分に発火する。同じトンネルを起動する機会が六十回あるということだ。コマンドが既存セッションを確認しない起動スクリプトなら、二番目の書き方はスケジュールの誤りではなく、所有者を競合させるループである。何よりも先に分フィールドを読むこと。

五つのフィールドは分、時、日、月、曜日の順で、最初のフィールドのアスタリスクは毎分を意味する。

分フィールドが回数を決める

先頭の数値が分なので、0 10 * * 3 は水曜日の十時台の最初の分である。これをアスタリスクに変えると時と曜日はそのままに一度だけという性質が失われ、六十件の一致が生まれる。cron は重複を排除しない。一致するたびにコマンドが再度起動される。これは cron の細かい話ではなくフィールドの意味論であり、導入されているスケジューラのリファレンスを読む価値がある。フィールドの順序と任意のユーザー名列がユーザー用とシステム用の crontab で異なるからだ。

一日かけて自分のタイムスタンプを記録する無害なコマンドで試すとよい。ログの一行ならスケジュール、一時間に六十行なら教訓である。

二度目の起動が一度目に何をするか

行儀のよい起動スクリプトは接続がすでに上がっていることに気づき、何もしない。生の openvpn 呼び出しはそうではない。別のプロセスを作り、デバイスやポートを掴もうとする別の試行を生み、同じ宛先を奪い合う別の経路集合まで作る。NetworkManager では、一つの接続に対する繰り返しの有効化要求は通常冪等であり、だからこそ誰かが別のコマンドを使うまで問題が表に出ない。スケジュールは、すでにトンネルを管理している所有者に問い合わせるべきである。

次に、接続の確立中に要求が何を意味するかを決める。二重の有効化も、保留中の起動の待ち行列も、明らかに正しいわけではない。どちらかを選び、スケジューラが偶然決めるのではなく起動スクリプトに実装させる。

コマンドの前にフィールドを数える

ユーザー用とシステム用の crontab は、コマンドの前にユーザー名の列があるかどうかが違う。両者の間でコピーしたエントリは、誤ったアカウントで動くか、ユーザー名をコマンドの最初の語として渡してしまう。手引きではなく導入済みのリファレンスを確認し、自分の行のフィールドを数えること。五つのフィールドの前に六番目の語があるならシステム用 crontab であり、同じ行をユーザー用 crontab に置くとその語をプログラムとして実行しようとする。

実行主体は環境をそのまま抱えている。cron ジョブは、解錠されたキーリングも、対話的なパスワード入力も、テストした作業ディレクトリも継承しない。

時計ではなく準備完了を待つ

起動に成功してもトンネルが動いているとは限らない。ジョブが終了する時点で、クライアントがまだエンドポイントを解決していたり、認証中だったり、経路を設定していたりする可能性がある。依存する処理は、固定の秒数ではなく、実際に必要な保護対象リソースを、上限付きのタイムアウトと記録される失敗とともに待つべきである。この違いが、たいてい動くスケジュールと、忙しい朝に静かに失敗するスケジュールを分ける。

その時間に対話的な MFA が必要なら、ジョブは人の注意を促せるが応答そのものは提供できない。再利用可能なパスワードを埋め込んだり無限に再試行したりせず、その状況を明示しておく。

本当に一度きりの有効化には、対応している単発の仕組みを使う。覚えておいて削除しなければならない繰り返しルールは、将来の二重起動である。

関連記事