KITA Eng.

北海道でサーバー技術者として歩み出したひとが綴るblog。

続・SSHクライアントといえば?

こんな記事を書いてから早半年以上が経過していました。 kitaeng.hateblo.jp

Windows 10だとなんだかPoderosaがうまく動いてくれなかったり、Ubuntuのターミナルでも自動でコマンドログを取る方法もググったら意外とたくさん出てきて、何とかなったので、すっかりPoderosaは使わなくなった今日この頃です。

この間、WindowsSSHクライアント環境に新兵器たちが現れ始めましたようです。ざっくり言ってしまうと、まだ、正式リリースになっていないものたちなので、実用性的には微妙な感じだけど、今後に期待というところ。

WindowsBashが?

だいぶ盛り上がりは去った感がありますが、今夏に予定されているWindows10の大型アップデート「Anniversary Update」の新機能の1つとして、「Bash on Windows」が話題に上って、ちょっと選択肢が増えたかもな状況でありました。(かくいう自分もこの新機能が気になって、Windows10のマシンをInsiderPreviewでアップデートするように設定を変えました。そして、直後にうまく起動しなくなるという事件に見舞われました...)

 これ-->鈴木淳也の「Windowsフロントライン」:Windows 10で動く「Bash」を試す Mac使いの開発者にもアピールする? (1/3) - ITmedia PC USER

PowershellにOpenSSHが移植されていた

すっかり冒頭の記事を書いている時には気が付いていませんでしたが、冒頭の記事を書いている時にはすでに、pre-release版で、「Win32 OpenSSH」なるものが、公開されていたようです。

 これ --> WindowsでOpenSSHを使ってSSHサーバーの起動と接続をする方法~PowerShell編 - Qiita  MSDNのblogにも記事になってた --> OpenSSH for Windows Update | Windows PowerShell Blog

開発はもりもり進んでいるようで、今夏ぐらいにはOpenSSHの本家の方にくっつくのを目指しているのだとか。昨冬に出たばかりのころは、.ssh/configファイルなんかが使えなくて...だったようですが、ここのところのアップデートで、configファイルは利くようになった模様。 github.com

普通にsshする分には、結構便利。何せ、gitからダウンロードしてきて、設置したPATHを環境変数に追加すればいいだけだから、WindowsBashより楽ちん(そりゃ、Bashの方はssh以外にもいろいろできちゃうんだから当たり前か...)。設定が悪いのか、ssh先でvimとか、topとか使うと画面表示が崩れるので、メイン使用にはできそうにない。設定をいじればなんとかなるんだろうか。

Cronの自動処理を多重起動させないための方法を調べたら...。

photo by mag3737

 Cronで定期的に実行さている処理が、システム負荷だったり処理する量の変化によって、実行間隔よりも長くかかってしまうと、多重起動するという小事件が発生するのです。

 多重起動しても大して影響のないようなものならいいんですけ、多重起動によって余計にシステム負荷が増大したり、多重で実行されることでうまく動かないなんてこともあったりするわけで...。

 必要に迫られたので、その解決方法をいろいろと探ってみました。

ひとまずGoogle先生に問い合わせると...

 上のようなところが現れました。

 大雑把にまとめると、

  1. ロックファイルをつくる
  2. プロセスの存在を確認する

という手段のもよう。まぁ、そうなりますよね...。

どう実装するか(方針)

 ロックファイルをつくるのが良いのか、プロセスの存在を確認するのが良いのか…。

 異常終了でロックファイルがうまく解放されないとか削除されないという事件が起こると悲しいことになるということで、プロセスの存在確認の方向で進むことにしました。

 プロセスの存在確認の方法もいくらか考えようがあって、

  1. pidファイルを用意して、起動時にpidを書き込む
  2. 現在稼働中のプロセスから拾う

とかがよく出てくるやつのようですが、1.はロックファイル同様ファイルの書き込みが発生するので、ロックファイルを却下した後に選択するのは...。ということで、2.現在稼働中のプロセスから拾う作戦でいくことにしました。

現在稼働中のプロセスから拾う作戦の実装

 いくつか候補が上がったので、ひとまず列挙。(以下、対象となるCronで実行するスクリプト名を、"cron_job.sh"という名称ということにします。)

  1. ps -ef | grep -E "[c]ron_job.sh"
  2. pgrep -fo cron_job.sh
  3. pidof -x cron_job.sh

 プロセスがあるかどうか拾うといえばpsと思ってましたが、他にもpgrepとかpidofなんてコマンドもいらっしゃったのですね。

1. ps -ef | grep -E "[c]ron_job.sh"

 プロセスの存在確認でよく見る気がする書き方ですが、おまじない的にgrep正規表現なんぞ使っていてうっとおしいので、却下とします。いや、別に正規表現が嫌いとかじゃないんだよ、謎の[ ]の意味を思い出すのがめんどくさいんだよ...。

2. pgrep -fo cron_job.sh

 Man page of PGREP

 -fオプションを使うことで、プロセス名だけではなくて、コマンドライン全体をマッチ対象にできる。これをつけるかつけないかがちょっとした分かれ目。

 cronで実行するスクリプトに実行権限をつけて実行させている場合は、-fなしでよさそうだけど、phpスクリプトとかで、/usr/bin/php /path/to/script/cron_job.phpみたいにcronで呼んでいると、cron_job.phpの部分は-fなしだとマッチしてくれないもよう(/usr/bin/phpの引数として与えられているだけの値という扱いだからだろう)。

 とりあえず-fつけときゃいいんじゃねと思いつつ、ちょっとした落とし穴もあって、-fをつけるとコマンドライン全体がマッチ対象になって、部分一致でも引っかかる。あまりないかもだけど、cron_job.shでマッチさせようとしたのに、cron_job.sh.v2とか謎のやつも動かしてたりすると...。多くの場合はほとんど気にならないパターンでしょうか。

 プロセスの抜出はこいつでうまくいきそうなので、実際に多重起動を防ぐ形でのcronへの登録は、

10 * * * * user if [ "$$" = "`pgrep -fo cron_job.sh`" ]; then /path/to/script/cron_job.sh; fi

こんな形としました。最初はは、testの部分を"" = "pgrep -fo cron_job.sh"としていたのですが、うまく動かず。cronで呼ばれた時点でpgrepには自分がマッチする状況になっているので、空にはならないということなんでしょう。一番古いpidと自分のpidが一致すれば自分しか起動していないという、判断をするという形にしました。

 ということで、今回は一応これで解決としました。

3. pidof -x cron_job.sh

 Man page of PIDOF

 2.で解決したので、こっちの検証はいらなかった気もしないでもないですが...。せっかくなので勉強がてらこっちでの方法も検討しました。

 こちらは、「名前でプロセスを見つけ、それらの PID を一覧表示する」というコマンドということで、2.の検証でちょっと問題に上った、

 cronで実行するスクリプトに実行権限をつけて実行させている場合は、-fなしでよさそうだけど、phpスクリプトとかで、/usr/bin/php /path/to/script/cron_job.phpみたいにcronで呼んでいると、cron_job.phpの部分は-fなしだとマッチしてくれないもよう(/usr/bin/phpの引数として与えられているだけの値という扱いだからだろう)。

が影響します。こいつで実装する場合は、スクリプトに実行権限を与えておいてあげて、直接実行してあげる形にする必要があるようです。

 で、このパターンで実際に多重起動を防ぐ形でのcronへの登録は、

10 * * * * user pidof -x cron_job.sh >/dev/null || /path/to/script/cron_job.sh

 こっちはすっきりしました。コマンドライン全体がマッチ条件にならないこと、指定したコマンドのプロセスがひとつも見つからなければ、0以外のリターンコードを返すを利用すると、ifを使わずに||を使ってあげれば、すっきり書けるもようです。

 cronで実行するスクリプト自体に実行権限がついているなら、こっちのほうが良いかもしれません。

雑感

 今回は、ファイルを読み書きせずに、既存のスクリプトには手を加えずに、なんか追加でインストールしたりせずにという条件の中での実装となったので、こんな感じです。  Google先生に尋ねているときにちらほら見かけたのは、あんまり間隔の短いスパンで動かすならデーモン化...みたいなこともあったりしたので、そういう方向もあるんでしょうねぇ。  よく使うコマンドをひたすらパイプで組み合わせていく作戦もいいんですけど、結構、要件にマッチしたコマンドって埋もれているもんだなぁと思ったのでありました。

whoisの情報から必要なところだけを抜き出してくれるようにしてみた。

何かとwhoisipアドレスを引いて情報を確認することが多いのですが、フォーマットのバラバラ具合にまいります。たいてい使うのは、当該IPを含むネットワークアドレスと国コードぐらい。これだけ出てくれればいいんす。なんてことを思って、早数カ月。別件で、探し物をしていたらPythonのパッケージに、ipwhoisなるものがあるのを発見しました。

なんかいい感じに必要なデータだけを抜き出すのに便利そうだったのでちょこっと書いてみました。

Simple Whois --> swhoisとか名付けてみた

ご自由にどうぞ。ただし、動作保証はできません。

実行例
user@localhost:~/$ ./swhois.py 122.226.102.60
122.226.102.60 --> 122.226.102.0/24 CN

引数に調べたいipを与えてあげると、whoisで調べて出てきた一番細かいCIDRで区切られたネットワークと国コードを返してくれます。

ipaddressモジュールを使用している関係で、python 2.x系では動きません。ipaddressモジュールを使わなくても実現できる気がしますが、ipaddressモジュールはとても便利なので無駄遣いしてしまします。

SoftEther VPN Serverのsystemd用*.serviceファイルを書いた

Ubuntu 16.04 LTSのリリースが目前に迫りつつある今日この頃(ちょいと気が早いか?)。何かにつけてそろそろsystemdと仲良くしないといけないようなので、initスクリプトではなくて、systemd用の*.serviceファイルについてごにょごにょし始めました。

第一弾として、個人的にとっつき易かった、SoftErther VPN Server用の.serviceファイルを書いてみました。

vpnserver.service

動作検証環境
メモ
  • KillMode=control-groupはデフォルトらしいが一応書いておく。KillMode=processにするときれいに消えてくれないので注意。
  • 一応ネットワーク関係がオンラインになってから立ち上がるようにしてみた。

SoftErther VPN Serverで使うSSL証明書をLet's Encryptで取得して設定するとこまで自動化してみた。

Let's Encryptの活用方法シリーズ!?第二弾です。今回は、SoftErther VPN Serverの証明書をLet's Encryptで取得して設定するところまでを自動でできるようにしてみました。

↓第一弾↓ kitaeng.hateblo.jp

内容的には、第一弾の自動で更新のスクリプトSoftErther VPNのコマンドラインツールでの証明書設定変更を追加しただけです。

softerthervpn_letsencrypt_cert_autoupdate.sh

動作検証環境
レポートメールのサンプル
Subject: [Soft Erther VPN Server Cert Auto Update] Update Report for exsample.com
Date: Sat, 20 Feb 2016 00:00:00 +0900 (JST)
From: root <root@exsample.com>
To: root@exsample.com

# Let's Encrypt Cert autopudate Start: 2016-02-20-Sat-00:00:00
# Update Log START ---------------------------------------------------------#
Updating letsencrypt and virtual environment dependencies......
Requesting root privileges to run with virtualenv: /root/.local/share/letsencrypt/bin/letsencrypt certonly --renew-by-default --webroot -w /var/www/html -d exsample.com
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/exsample.com/fullchain.pem. Your cert will
   expire on 2016-05-20. To obtain a new version of the certificate in
   the future, simply run Let's Encrypt again.
 - If you like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le


# SoftErther VPN SERVER CERT CHANGE LOG START ------------------------------#
vpncmd コマンド - SoftEther VPN コマンドライン管理ユーティリティ
SoftEther VPN コマンドライン管理ユーティリティ (vpncmd コマンド)
Version 4.19 Build 9599   (Japanese)
Compiled 2015/10/19 20:28:20 by yagi at pc30
Copyright (c) SoftEther VPN Project. All Rights Reserved.

VPN Server "localhost" (ポート 5555) に接続しました。

VPN Server 全体の管理権限があります。

VPN Server>ServerCertSet /LOADCERT:/etc/letsencrypt/live/exsample.com/fullchain.pem /LOADKEY:/etc/letsencrypt/live/exsample.com/privkey.pem
ServerCertSet コマンド - VPN Server の SSL 証明書と秘密鍵の設定
コマンドは正常に終了しました。

#---------------------------- SoftErther VPN SERVER CERT CHANGE LOG END ---#
#------------------------------------------------------------- Update Log END ---#
# Let's Encrypt Cert autopudate End: 2016-02-20-Sat-00:00:00
諸注意

うまく証明書が発行されなかったときや、SoftErther VPN Serverのパスワードが違うなどがあると、SoftErther VPNコマンドラインツールが入力待ちの状態になってしまうので、そこでもれなく止まって進まなくなります……。設定値には十分ご注意を。

あとがき

実験で何回か実行したら、Lets's Encryptのエラーで処理が止まってしまった。リクエストしすぎだよ!って怒られました。ごめんなさい。