ssh環境での誤ったシステム停止を防ぐmolly-guard

最近 shutdown の man を見比べたりしてたのですが,そのときに systemd, sysvinit 以外に molly-guard という見知らぬものが.

$ apt-file search /sbin/shutdown
molly-guard: /sbin/shutdown
systemd-sysv: /sbin/shutdown
sysvinit-core: /sbin/shutdown

パッケージ情報をみると shutdown コマンドなどを置き換えて ssh 接続時には確認のためにホスト名を聞くようになるようです.
これにより手元のPCの再起動をしたつもりがリモートのサーバを再起動してしまうなどといったことが防げるようになります.

$ apt show molly-guard
Package: molly-guard
Version: 0.6.4
Priority: extra
Section: admin
Maintainer: Francois Marier <francois@debian.org>
Installed-Size: 57.3 kB
Depends: procps
Enhances: init, kexec-tools, mosh, openssh-server, pm-utils, systemd, sysvinit, upstart
Tag: implemented-in::shell, interface::commandline, network::server,
 protocol::ssh, role::program, scope::utility
Download-Size: 13.8 kB
APT-Manual-Installed: yes
APT-Sources: http://ftp.jp.debian.org/debian stretch/main amd64 Packages
Description: protects machines from accidental shutdowns/reboots
 The package installs a shell script that overrides the existing
 shutdown/reboot/halt/poweroff/coldreboot/pm-hibernate/pm-suspend* commands
 and first runs a set of scripts, which all have to exit successfully,
 before molly-guard invokes the real command.
 .
 One of the scripts checks for existing SSH sessions. If any of the four
 commands are called interactively over an SSH session, the shell script
 prompts you to enter the name of the host you wish to shut down. This should
 adequately prevent you from accidental shutdowns and reboots.
 .
 molly-guard diverts the real binaries to /lib/molly-guard/.  You can bypass
 molly-guard by running those binaries directly.

早速試してみます.

$ sudo apt install molly-guard
    :
package diverts others to: /lib/molly-guard/coldreboot
/sbin/halt
package diverts others to: /lib/molly-guard/halt
/sbin/pm-hibernate
/sbin/pm-suspend
/sbin/pm-suspend-hybrid
/sbin/poweroff
package diverts others to: /lib/molly-guard/poweroff
/sbin/reboot
package diverts others to: /lib/molly-guard/reboot
/sbin/shutdown
package diverts others to: /lib/molly-guard/shutdown

このあたりのコマンドが置き換わっています.

$ ls -l /sbin | grep molly
lrwxrwxrwx 1 root root        28 Aug 16  2016 coldreboot -> /lib/molly-guard/molly-guard
lrwxrwxrwx 1 root root        28 Aug 16  2016 halt -> /lib/molly-guard/molly-guard
lrwxrwxrwx 1 root root        28 Aug 16  2016 pm-hibernate -> /lib/molly-guard/molly-guard
lrwxrwxrwx 1 root root        28 Aug 16  2016 pm-suspend -> /lib/molly-guard/molly-guard
lrwxrwxrwx 1 root root        28 Aug 16  2016 pm-suspend-hybrid -> /lib/molly-guard/molly-guard
lrwxrwxrwx 1 root root        28 Aug 16  2016 poweroff -> /lib/molly-guard/molly-guard
lrwxrwxrwx 1 root root        28 Aug 16  2016 reboot -> /lib/molly-guard/molly-guard
lrwxrwxrwx 1 root root        28 Aug 16  2016 shutdown -> /lib/molly-guard/molly-guard

コマンド類は /lib/molly-guard 以下に退避されるようです.

sysvinit
$ ls -lA /lib/molly-guard
total 48
-rwxr-xr-x 1 root root 18952 Feb 13  2017 halt
-rwxr-xr-x 1 root root  2767 Aug 16  2016 molly-guard
lrwxrwxrwx 1 root root     4 Feb 13  2017 poweroff -> halt
lrwxrwxrwx 1 root root     4 Feb 13  2017 reboot -> halt
-rwxr-xr-x 1 root root 23368 Feb 13  2017 shutdown
systemd
$ ls -lA /lib/molly-guard
total 4
lrwxrwxrwx 1 root root   14 Jun 14 05:20 halt -> /bin/systemctl
-rwxr-xr-x 1 root root 2767 Aug 16  2016 molly-guard
lrwxrwxrwx 1 root root   14 Jun 14 05:20 poweroff -> /bin/systemctl
lrwxrwxrwx 1 root root   14 Jun 14 05:20 reboot -> /bin/systemctl
lrwxrwxrwx 1 root root   14 Jun 14 05:20 shutdown -> /bin/systemctl

ssh 経由で shutdown(sysvinit) を試みるとこのように hostname を求められます.ここで誤った hostname を書くと shutdown がキャンセルされました.

$ sudo shutdown -f -P -h +10 "kernel update (`uname -r`)"
W: molly-guard: SSH session detected!
Please type in hostname of the machine to shutdown: desktop
Good thing I asked; I won't shutdown debian ...
W: aborting shutdown due to 30-query-hostname exiting with code 1.

正しい hostname を指定すると shutdown が呼ばれます.

$ sudo shutdown -f -P -h +10 "kernel update (`uname -r`)"
W: molly-guard: SSH session detected!
Please type in hostname of the machine to shutdown: debian

Broadcast message from root@debian (pts/0) (Thu Sep 27 06:15:28 2018):

kernel update (4.9.0-3-amd64)
The system is going DOWN for system halt in 10 minutes!
^C
Shutdown cancelled.

ssh経由でない場合は molly-guard はすぐに shutdown を呼びます.

$ sudo shutdown -f -P -h +10 'poweroff'
^C
Shutdown cancelled.

環境

$ dpkg-query -W systemd-sysv molly-guard
molly-guard     0.6.4
systemd-sysv    232-25+deb9u4
$ dpkg-query -W sysvinit-core
sysvinit-core   2.88dsf-59.9
$ cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
NAME="Debian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
$ cat /etc/debian_version
9.5
$ uname -m
x86_64

qcow2 イメージをマウント

qemuやKVMでよく使われるqcow2イメージのマウントを行いました.

イメージの確認
$ sudo qemu-img info /var/lib/libvirt/images/Debian.qcow2
image: /var/lib/libvirt/images/Debian.qcow2
file format: qcow2
virtual size: 20G (21474836480 bytes)
disk size: 20G
cluster_size: 65536
Format specific information:
	compat: 1.1
	lazy refcounts: true
	refcount bits: 16
	corrupt: false
nbd モジュールの読み込み
$ sudo modprobe nbd
qcow2 イメージのバインド
$ sudo qemu-nbd -c /dev/nbd0 /var/lib/libvirt/images/Debian.qcow2
$ sudo fdisk -l /dev/nbd0
ディスク /dev/nbd0: 20 GiB, 21474836480 バイト, 41943040 セクタ
単位: セクタ (1 * 512 = 512 バイト)
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト
ディスクラベルのタイプ: dos
ディスク識別子: 0x3c0808c8

デバイス    起動 開始位置 終了位置   セクタ サイズ Id タイプ
/dev/nbd0p1 *        2048   499711   497664   243M 83 Linux
/dev/nbd0p2        501758 41940991 41439234  19.8G  5 拡張領域
/dev/nbd0p5        501760 41940991 41439232  19.8G 83 Linux
kpartx でデバイスマッピング
$ sudo kpartx /dev/nbd0
nbd0p1 : 0 497664 /dev/nbd0 2048
nbd0p2 : 0 2 /dev/nbd0 501758
nbd0p5 : 0 41439232 /dev/dm-1 2
dm-crypt で暗号化されているのでデバイスをオープン
$ sudo cryptsetup open /dev/nbd0p5 crypt
Enter passphrase for /dev/nbd0p5:
lvm領域なのでスキャンする
$ sudo lvmdiskscan
    :
  /dev/mapper/crypt                                     [     <19.76 GiB] LVM physical volume
  /dev/nbd0p5                                           [     <19.76 GiB]
  /dev/debian-vg/root                                   [      18.75 GiB]
  /dev/debian-vg/swap_1                                 [       1.00 GiB]
    :
マウントして内容確認
$ sudo mount -o ro /dev/debian-vg/root /mnt
$ ls /mnt/
bin   dev  home        initrd.img.old  lib64       media  opt   root  sbin  sys  usr  vmlinuz
boot  etc  initrd.img  lib             lost+found  mnt    proc  run   srv   tmp  var  vmlinuz.old
Table of Contents

環境

$ dpkg-query -W kpartx qemu-utils fdisk cryptsetup lvm2 mount linux-image-`uname -r`
cryptsetup      2:2.0.4-2
fdisk   2.32.1-0.1
kpartx  0.7.7-1
linux-image-4.18.0-1-amd64      4.18.8-1
lvm2    2.02.176-4.1
mount   2.32.1-0.1
qemu-utils      1:2.12+dfsg-3
$ lsb_release -d
Description:    Debian GNU/Linux unstable (sid)
$ uname -m
x86_64

Linuxの Google Chrome/Chromium でサインインと同期を無効にする

を見てなるほどと思ったのだけど,macOS, Windows の手順はあるけれどLinux の手順がないなてことで調べてみました.

参考にしたのはこちら.

/etc/opt/chrome, /etc/opt/chromium 以下に json 形式で設定ファイルを設定するだけでした.

Google Chrome の場合

ディレクトリ作成
$ sudo mkdir -p /etc/opt/chrome/policies/managed /etc/opt/chrome/policies/recommended
/etc/opt/chrome/policies/managed/disablesync.json に設定ファイル設置
{
        "SyncDisabled":true,
        "RestrictSigninToPattern":".*@example.com"
}

Chromium の場合

ディレクトリ作成
$ sudo mkdir -p /etc/opt/chromium/policies/managed /etc/opt/chromium/policies/recommended
/etc/opt/chromium/policies/managed/disablesync.json に設定ファイル設置
{
        "SyncDisabled":true,
        "RestrictSigninToPattern":".*@example.com"
}

環境

$ dpkg-query -W google-chrome-stable chromium
chromium        69.0.3497.92-1
google-chrome-stable    69.0.3497.100-1
$ lsb_release -d
Description:    Debian GNU/Linux unstable (sid)
$ uname -m
x86_64

CLI なワンタイムパスワードマネージャの cloak を試す

Rust製のコマンドラインで動作するワンタイムパスワードマネージャです.TOTP/HOTP に対応しています.

Table of Contents

導入

Rust package managerのcargoを導入
$ sudo apt install cargo
cargoでcloakを導入
$ cargo install cloak

~/.cargo/ 以下に導入される

試してみる

help
$ ~/.cargo/bin/cloak --help
cloak 0.1.0
Evans Murithi <murithievans80@gmail.com>
A Command Line OTP Authenticator application.

USAGE:
	cloak [SUBCOMMAND]

FLAGS:
	-h, --help       Prints help information
	-V, --version    Prints version information

SUBCOMMANDS:
	add               Add a new account
	delete            Delete an account
	help              Prints this message or the help of the given subcommand(s)
	list              List OTP for all accounts
	recovery_codes    View recovery codes for an account
	view              View the OTP for an account
add サブコマンドで登録する
$ ~/.cargo/bin/cloak add --help
cloak-add
Add a new account

USAGE:
	cloak add [FLAGS] [OPTIONS] <account> <key>

FLAGS:
	-h, --help       Prints help information
		--hotp       Counter based account
		--totp       Time based account (default)
	-V, --version    Prints version information

OPTIONS:
	-a, --algorithm <ALGORITHM>    Algorithm to use to generate the OTP code [possible values: SHA1, SHA256, SHA384,
								   SHA512, SHA512_256]

ARGS:
	<account>    Name of the account
	<key>        Secret key of the OTP
$ ~/.cargo/bin/cloak add matoken@home.nextcloud M0KMYFVOZY7AGX47
Account successfully created
登録情報確認
$ ~/.cargo/bin/cloak list
Account: matoken@home.nextcloud
TOTP: 538273
アカウントを指定してTOTPトークン表示
$ ~/.cargo/bin/cloak view matoken@home.nextcloud
370696
設定ファイルには生で情報が格納されるので取扱注意
$ cat ~/.cloak/accounts
["matoken@home.nextcloud"]
key = "M0KMYFVOZY7AGX47"
totp = true
hash_function = "SHA1"
アクセス権もそのままでは危ないので注意
$ ls -l ~/.cloak/accounts
-rw-r--r-- 1 matoken matoken 87  9月 22 06:19 /home/matoken/.cloak/accounts
$ chmod -R go-rx ~/.cloak
$ chattr +s ~/.cloak/accounts
$ ls -la ~/.cloak
合計 12
drwx------  2 matoken matoken 4096  9月 22 06:19 .
drwxr-xr-x 62 matoken matoken 4096  9月 23 19:58 ..
-rw-------  1 matoken matoken   87  9月 22 06:19 accounts

秘密鍵が平文でそのままファイルに格納されるので最低でもecryptfsは設定しておかないとかなな感じ?

私は現在はoathtoolを使ったscriptを使っています.秘密鍵はファイルに手で書いて暗号化しておきscript実行時に復号してoathtoolに渡してTOTP表示という感じ.若しくはKeePassCX.

環境

$ ~/.cargo/bin/cloak -V
cloak 0.1.0
$ dpkg-query -W cargo
cargo   0.29.0-1
$ lsb_release -d
Description:    Debian GNU/Linux unstable (sid)
$ uname -m
x86_64

Nextcloud 14 の Telegram を使った2要素認証

先日ファイル共有ソフトの Nextcloud 14がリリースされました.

新機能のうちSignal/Telegram/SMS による2要素認証のサポートが気になります.

Signal/Telegram/SMS 2FA support
A new 2-factor authentication provider named ‘gateway’ was introduced which allows users to use the secure messaging apps Signal and Telegram as well as various SMS gateways as second factor to secure their authentication.

SMSはplaySMS, websms.deを利用するようです.SignalもTelegramもセキュアなメッセージングサービスなのでこういう用途に向いていそうです.どちらも公式でLinux x86_64のデスクトップアプリが提供されています.アプリの出来は今の所Telegramのほうが上だと思います.Arm Linuxだと公式のものはないのですが,TelegramについてはCLIのアプリがあり,Raspberry Pi の Raspbian stretch でも要patchで動作しました.(Webアプリもあるのでそちらでも動作すると思うが未確認)

SMSはやったことがあるし,Telegram が使いやすい.てことでTelegramの設定をしてみました.まだ未実装な機能が多くちょっと面倒なのでメモしておきます.

Two-Factor Gateway の導入

Nextcloud 14に Two-Factor Gateway アプリを導入します.管理者アカウントで「アプリ」の「セキュリティ」から導入するのが楽でしょう.

導入するとNextcloudの「設定」の「セキュリティ」の中に「Message gateway second-factor auth」という項目が現れますが,設定が出来ません.

GitHubのドキュメントをみると未だ未実装の機能が多いようです.

Telegram Bot の作成

TOTPコードの送信する Telegram Bot を作成します.作成はTelegram のドキュメントを参照して作成します.

BotFather と会話してbot作成

BotFather という bot 管理用の bot が居るので,会話してbot を作成します.

アイコンちょっと怖い

44062365934 5aabe8f64d m

/newbot コマンドで新しい bot の作成が始まります.botの名前を効かれるので答えます.続いて bot の username を求められます.この username は最後に bot とつける必要があります.
成功するとアクセストークンが表示されるのでメモして次の手旬に移ります.以下の例では 640093430:AAFTa_pSAcKCZWeFoVDt-l7h7ewqzNe0Luo がトークンです.

Done! Congratulations on your new bot. You will find it at t.me/matoken_bot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this.

Use this token to access the HTTP API:
640093430:AAFTa_pSAcKCZWeFoVDt-l7h7ewqzNe0Luo

For a description of the Bot API, see this page: https://core.telegram.org/bots/api

30910879338 4609b7a707
44781979421 aac86c72de

occ コマンドで Telegram bot のトークン設定

Nextcloud のインストールされている場所に Nextcloud 管理用の occ コマンドがあります.このコマンドで Telegram bot のトークンを設定します.
トークンの 640093430: 部分は省いてその後ろだけを入力します.

$ sudo -u www-data php ./occ twofactorauth:gateway:configure telegram
Please enter your Telegram bot token: AAFTa_pSAcKCZWeFoVDt-l7h7ewqzNe0Luo
Using AAFTa_pSAcKCZWeFoVDt-l7h7ewqzNe0Luo.
$ sudo -u www-data php ./occ twofactorauth:gateway:status
Signal gateway: not configured
SMS gateway: not configured
Telegram gateway: configured

ひとまずこれで管理者側の設定は終了です.

Telegram bot の chat_id を調べて登録する

ここからはNextcloud の利用アカウントでの操作になります.

Telegram
In order to receive authentication codes via Telegram, you first have to start a new chat with the bot set up by your admin.
Secondly, you have to obtain your Telegram ID via the ID Bot. Enter this ID to receive your verification code below.

You are not using Telegram for two-factor authentication at the moment. Enable

Telegram アプリを利用して,Telegram の bot から TOTPコードを受け取りたい Telegram アカウントに適当なメッセージを投げます.

@matoken hello

その後,Telegram API を利用してメッセージを取得して chat_id を調べます.
以下の例では 475721977 です.

$ curl https://api.telegram.org/bot640093430:AAHu9u_c12KC2PY8g22QZoA94u4tAJvxsvY/getUpdates | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   358  100   358    0     0    315      0  0:00:01  0:00:01 --:--:--   315
{
  "ok": true,
  "result": [
    {
      "update_id": 612249686,
      "message": {
        "message_id": 9,
        "from": {
          "id": 475721977,
          "is_bot": false,
          "first_name": "matoken",
          "username": "matoken",
          "language_code": "En"
        },
        "chat": {
          "id": 475721977,
          "first_name": "matoken",
          "username": "matoken",
          "type": "private"
        },
        "date": 1537282406,
        "text": "@matoken hello",
        "entities": [
          {
            "offset": 0,
            "length": 8,
            "type": "mention"
          }
        ]
      }
    }
  ]
}

試しに送信してみて Telegram アプリに bot からメッセージが飛んできたら chat id が正しいはず.

$ curl -X POST "https://api.telegram.org/bot640093430:AAHu9u_c12KC2PY8g22QZoA94u4tAJvxsvY" -d "chat_id=475721977&text=hello" | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   274  100   246  100    28    179     20  0:00:01  0:00:01 --:--:--   200
{
  "ok": true,
  "result": {
    "message_id": 15,
    "from": {
      "id": 640093430,
      "is_bot": true,
      "first_name": "matoken_bot",
      "username": "matoken_bot"
    },
    "chat": {
      "id": 475721977,
      "first_name": "matoken",
      "username": "matoken",
      "type": "private"
    },
    "date": 1537312183,
    "text": "hello"
  }
}

Nextcloud にログインして,「設定」→「セキュリティ」の下部に「Message gateway second-factor auth」という項目が出来ているので「Enable」を押す.

Telegram
In order to receive authentication codes via Telegram, you first have to start a new chat with the bot set up by your admin.
Secondly, you have to obtain your Telegram ID via the ID Bot. Enter this ID to receive your verification code below.

You are not using Telegram for two-factor authentication at the moment. Enable

以下のメッセージが表示されたら chat_id を入力して Verify します.

Enter your identification (e.g. phone number to start the verification):
 Verify

Telegram で TOTPコードが飛んで来るのでそれを入力したらOKです.
一旦ログアウトして試してみましょう.

Telegram 認証を試す

いつものようにユーザ名,パスワードで認証すると以下のような画面に遷移します.
「Authenticate via Telegram」を押します.

44781965201 94bd9ca33e

Telegram から認証コードが飛んでくるのでそれを入力して認証完了です.

30910872108 2d5e8c21eb

めんどくさい……

未だ手順が面倒で一般ユーザに試してくれと言える状態ではないですね.でも次のリリースあたりでは簡単になるんじゃないでしょうか.

環境

$ sudo -u www-data php ./occ app:list|grep twofactor_|grep :
  - twofactor_backupcodes: 1.3.1
  - twofactor_gateway: 0.9.0
  - twofactor_totp: 1.5.0
$ sudo -u www-data php ./occ -V
Nextcloud 14.0.0
$ lsb_release -d
Description:    Ubuntu 16.04.5 LTS
$ uname -m
x86_64

Telegram の非公式cli client の telegram-cli を試す

Arm でも動くかな?と試してみました.

必要パッケージの導入
$ sudo apt install libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev libjansson-dev libpython-dev make libssl1.0-dev
sourceをcloneする
$ git clone --recursive https://github.com/vysheng/tg.git && cd tg
configure & make
$ ./configure
$ make
build error1
tgl/crypto/rsa_pem_openssl.c: In function ‘TGLC_rsa_new’:
tgl/crypto/rsa_pem_openssl.c:41:6: error: dereferencing pointer to incomplete type ‘RSA {aka struct rsa_st}’
   ret->e = unwrap_bn (TGLC_bn_new ());
      ^~
tgl/crypto/rsa_pem_openssl.c: In function ‘TGLC_rsa_n’:
tgl/crypto/rsa_pem_openssl.c:52:1: error: control reaches end of non-void function [-Werror=return-type]
 RSA_GETTER(n);
 ^~~~~~~~~~
tgl/crypto/rsa_pem_openssl.c: In function ‘TGLC_rsa_e’:
tgl/crypto/rsa_pem_openssl.c:53:1: error: control reaches end of non-void function [-Werror=return-type]
 RSA_GETTER(e);
 ^~~~~~~~~~
cc1: all warnings being treated as errors
Makefile.tgl:20: recipe for target 'objs/crypto/rsa_pem_openssl.o' failed
make: *** [objs/crypto/rsa_pem_openssl.o] Error 1

libssl-dev(1.1.1)では動作しないのでlibssl1.0-devに入れ替える. sudo apt install libssl1.0-dev

build error2
gcc -I. -I. -I./tgl -w  -I/usr/local/include -I/usr/include -I/usr/include -I/usr/include/lua5.2  -DHAVE_CONFIG_H -Wall -Werror -Wextra -Wno-missing-field-initializers -Wno-deprecated-declarations -fno-strict-aliasing -fno-omit-frame-pointer -ggdb -Wno-unused-parameter -fPIC -iquote ./tgl/tl-parser -c -MP -MD -MF dep/tl-parser.d -MQ objs/tl-parser.o -o objs/tl-parser.o tgl/tl-parser/tl-parser.c
tgl/tl-parser/tl-parser.c:37:10: fatal error: zlib.h: そのようなファイルやディレクトリはありません
 #include <zlib.h>
          ^~~~~~~~
compilation terminated.
make: *** [Makefile.tl-parser:4: objs/tl-parser.o] エラー 1

zlib1g-dev を導入する. sudo apt install zlib1g-dev

Raspbian stretchでの実行時エラー(32bit環境での不具合?)
$ bin/telegram-cli
Telegram-cli version 1.4.1, Copyright (C) 2013-2015 Vitaly Valtman
Telegram-cli comes with ABSOLUTELY NO WARRANTY; for details type `show_license'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show_license' for details.
Telegram-cli uses libtgl version 2.1.0
Telegram-cli includes software developed by the OpenSSL Project
for use in the OpenSSL Toolkit. (http://www.openssl.org/)
I: config dir=[/home/pi/.telegram-cli]
> telegram-cli: tgl/mtproto-utils.c:101: BN2ull: Assertion `0' failed.
SIGNAL received

以下の修正を行い, make clean して build しなおす.

$ diff -ud tgl/mtproto-utils.c.org tgl/mtproto-utils.c
--- tgl/mtproto-utils.c.org     2018-09-19 21:01:44.878560077 +0900
+++ tgl/mtproto-utils.c 2018-09-19 20:59:55.778295550 +0900
@@ -98,13 +98,13 @@
   if (sizeof (unsigned long) == 8) {
	 return TGLC_bn_get_word (b);
   } else if (sizeof (unsigned long long) == 8) {
-    assert (0); // As long as nobody ever uses this code, assume it is broken.
+    assert (0);  As long as nobody ever uses this code, assume it is broken.
	 unsigned long long tmp;
	 /* Here be dragons, but it should be okay due to be64toh */
	 TGLC_bn_bn2bin (b, (unsigned char *) &tmp);
	 return be64toh (tmp);
   } else {
-    assert (0);
+//    assert (0);
   }
 }
$ make clean
$ ./configure
$ make
初回実行時はSMS認証が必要
$ bin/telegram-cli
Telegram-cli version 1.4.1, Copyright (C) 2013-2015 Vitaly Valtman
Telegram-cli comes with ABSOLUTELY NO WARRANTY; for details type `show_license'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show_license' for details.
Telegram-cli uses libtgl version 2.1.0
Telegram-cli includes software developed by the OpenSSL Project
for use in the OpenSSL Toolkit. (http://www.openssl.org/)
I: config dir=[/home/mk/.telegram-cli]
[/home/mk/.telegram-cli] created
[/home/mk/.telegram-cli/downloads] created
phone number: +81-9000000000
code ('CALL' for phone code): 16802
User matoken updated flags
User matoken online (was online [2018/09/18 23:55:32])
User matoken offline (was online [2018/09/18 23:50:35])
認証を行うとメッセージが飛んでくる
matoken,

We detected a login into your account from a new device on 19/09/2018 at 12:28:55 UTC.

Device: Unix Console
Location: Shizuoka, Japan (IP = 180.131.110.140)

If this wasn't you, you can go to Settings - Privacy and Security - Sessions and terminate that session.

If you think that somebody logged in to your account against your will, you can enable two-step verification in Privacy and Security settings.

Sincerely,
The Telegram Team
メッセージを送信してみる
> msg @matoken hello
telegram-cli の help
$ bin/telegram-cli --help
telegram-cli Usage
  --phone/-u                           specify username (would not be asked during authorization)
  --rsa-key/-k                         specify location of public key (possible multiple entries)
  --verbosity/-v                       increase verbosity (0-ERROR 1-WARNIN 2-NOTICE 3+-DEBUG-levels)
  --enable-msg-id/-N                   message num mode
  --config/-c                          config file name
  --profile/-p                         use specified profile
  --log-level/-l                       log level
  --sync-from-start/-f                 during authorization fetch all messages since registration
  --disable-auto-accept/-E             disable auto accept of encrypted chats
  --lua-script/-s                      lua script file
  --wait-dialog-list/-W                send dialog_list query and wait for answer before reading input
  --disable-colors/-C                  disable color output
  --disable-readline/-R                disable readline
  --alert/-A                           enable bell notifications
  --daemonize/-d                       daemon mode
  --logname/-L <log-name>              log file name
  --username/-U <user-name>            change uid after start
  --groupname/-G <group-name>          change gid after start
  --disable-output/-D                  disable output
  --tcp-port/-P <port>                 port to listen for input commands
  --udp-socket/-S <socket-name>        unix socket to create
  --exec/-e <commands>                 make commands end exit
  --disable-names/-I                   use user and chat IDs in updates instead of names
  --enable-ipv6/-6                     use ipv6 (may be unstable)
  --help/-h                            prints this help
  --accept-any-tcp                     accepts tcp connections from any src (only loopback by default)
  --disable-link-preview               disables server-side previews to links
  --bot/-b                             bot mode
  --json                               prints answers and values in json format
  --permanent-msg-ids                  use permanent msg ids
  --permanent-peer-ids                 use permanent peer ids
telegram-cli 起動時のhelp
$ bin/telegram-cli -e help
Telegram-cli version 1.4.1, Copyright (C) 2013-2015 Vitaly Valtman
Telegram-cli comes with ABSOLUTELY NO WARRANTY; for details type `show_license'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show_license' for details.
Telegram-cli uses libtgl version 2.1.0
Telegram-cli includes software developed by the OpenSSL Project
for use in the OpenSSL Toolkit. (http://www.openssl.org/)
I: config dir=[/home/pi/.telegram-cli]
[33;1maccept_secret_chat <secret chat>  Accepts secret chat. Only useful with -E option
add_contact <phone> <first name> <last name>    Tries to add user to contact list
block_user <user>       Blocks user
broadcast <user>+ <text>        Sends text to several users at once
channel_get_admins <channel> [limit=100] [offset=0]     Gets channel admins
channel_get_members <channel> [limit=100] [offset=0]    Gets channel members
channel_info <channel>  Prints info about channel (id, members, admin, etc.)
channel_invite <channel> <user> Invites user to channel
channel_join <channel>  Joins to channel
channel_kick <channel> <user>   Kicks user from channel
channel_leave <channel> Leaves from channel
channel_list [limit=100] [offset=0]     List of last channels
channel_set_about <channel> <about>     Sets channel about info.
channel_set_admin <channel> <admin> <type>      Sets channel admin. 0 - not admin, 1 - moderator, 2 - editor
channel_set_username <channel> <username>       Sets channel username info.
channel_set_photo <channel> <filename>  Sets channel photo. Photo will be cropped to square
chat_add_user <chat> <user> [msgs-to-forward]   Adds user to chat. Sends him last msgs-to-forward message from this chat. Default 100
chat_del_user <chat> <user>     Deletes user from chat
chat_info <chat>        Prints info about chat (id, members, admin, etc.)
chat_set_photo <chat> <filename>        Sets chat photo. Photo will be cropped to square
chat_upgrade <chat>     Upgrades chat to megagroup
chat_with_peer <peer>   Interface option. All input will be treated as messages to this peer. Type /quit to end this mode
clear   Clears all data and exits. For debug.
contact_list    Prints contact list
contact_search username Searches user by username
create_channel <name> <about> <user>+   Creates channel with users
create_group_chat <name> <user>+        Creates group chat with users
create_secret_chat <user>       Starts creation of secret chat
del_contact <user>      Deletes contact from contact list
delete_msg <msg-id>     Deletes message
dialog_list [limit=100] [offset=0]      List of last conversations
export_card     Prints card that can be imported by another user with import_card method
export_channel_link     Prints channel link that can be used to join to channel
export_chat_link        Prints chat link that can be used to join to chat
fwd <peer> <msg-id>+    Forwards message to peer. Forward to secret chats is forbidden
fwd_media <peer> <msg-id>       Forwards message media to peer. Forward to secret chats is forbidden. Result slightly differs from fwd
get_terms_of_service    Prints telegram's terms of service
get_message <msg-id>    Get message by id
get_self        Get our user info
help [command]  Prints this help
history <peer> [limit] [offset] Prints messages with this peer (most recent message lower). Also marks messages as read
import_card <card>      Gets user by card and prints it name. You can then send messages to him as usual
import_chat_link <hash> Joins to chat by link
import_channel_link <hash>      Joins to channel by link
load_audio <msg-id>     Downloads file to downloads dirs. Prints file name after download end
load_channel_photo <channel>    Downloads file to downloads dirs. Prints file name after download end
load_chat_photo <chat>  Downloads file to downloads dirs. Prints file name after download end
load_document <msg-id>  Downloads file to downloads dirs. Prints file name after download end
load_document_thumb <msg-id>    Downloads file to downloads dirs. Prints file name after download end
load_file <msg-id>      Downloads file to downloads dirs. Prints file name after download end
load_file_thumb <msg-id>        Downloads file to downloads dirs. Prints file name after download end
load_photo <msg-id>     Downloads file to downloads dirs. Prints file name after download end
load_user_photo <user>  Downloads file to downloads dirs. Prints file name after download end
load_video <msg-id>     Downloads file to downloads dirs. Prints file name after download end
load_video_thumb <msg-id>       Downloads file to downloads dirs. Prints file name after download end
main_session    Sends updates to this connection (or terminal). Useful only with listening socket
mark_read <peer>        Marks messages with peer as read
msg <peer> <text>       Sends text message to peer
msg <peer> <kbd> <text> Sends text message to peer with custom kbd
post <peer> <text>      Sends text message to peer as admin
post_audio <peer> <file>        Posts audio to peer
post_document <peer> <file>     Posts document to peer
post_file <peer> <file> Sends document to peer
post_location <peer> <latitude> <longitude>     Sends geo location
post_photo <peer> <file>       Sends photo to peer
post_text <peer> <file> Sends contents of text file as plain text message
post_video <peer> <file>       Sends video to peer
quit    Quits immediately
rename_channel <channel> <new name>     Renames channel
rename_chat <chat> <new name>   Renames chat
rename_contact <user> <first name> <last name>  Renames contact
reply <msg-id> <text>   Sends text reply to message
reply_audio <msg-id> <file>     Sends audio to peer
reply_contact <msg-id> <phone> <first-name> <last-name> Sends contact (not necessary telegram user)
reply_document <msg-id> <file>  Sends document to peer
reply_file <msg-id> <file>      Sends document to peer
reply_location <msg-id> <latitude> <longitude>  Sends geo location
reply_photo <msg-id> <file>    Sends photo to peer
reply_video <msg-id> <file>     Sends video to peer
resolve_username username       Searches user by username
safe_quit       Waits for all queries to end, then quits
search [peer] [limit] [from] [to] [offset] pattern      Search for pattern in messages from date from to date to (unixtime) in messages with peer (if peer not
present, in all messages)
send_audio <peer> <file>        Sends audio to peer
send_contact <peer> <phone> <first-name> <last-name>    Sends contact (not necessary telegram user)
send_document <peer> <file>     Sends document to peer
send_file <peer> <file> Sends document to peer
send_location <peer> <latitude> <longitude>     Sends geo location
send_photo <peer> <file>       Sends photo to peer
send_text <peer> <file> Sends contents of text file as plain text message
send_typing <peer> [status]     Sends typing notification. You can supply a custom status (range 0-10): none, typing, cancel, record video, upload video, recor
d audio, upload audio, upload photo, upload document, geo, choose contact.
send_typing_abort <peer>        Sends typing notification abort
send_video <peer> <file>       Sends video to peer
set <param> <value>     Sets value of param. Currently available: log_level, debug_verbosity, alarm, msg_num
set_password <hint>     Sets password
set_profile_name <first-name> <last-name>       Sets profile name.
set_profile_photo <filename>    Sets profile photo. Photo will be cropped to square
set_ttl <secret chat>   Sets secret chat ttl. Client itself ignores ttl
set_username <name>     Sets username.
set_phone_number <phone>        Changes the phone number of this account
show_license    Prints contents of GPL license
start_bot <bot> <chat> <data>   Adds bot to chat
stats   For debug purpose
status_online   Sets status as online
status_offline  Sets status as offline
unblock_user <user>     Unblocks user
user_info <user>        Prints info about user (id, last online, phone)
version Prints client and library version
view_audio <msg-id>     Downloads file to downloads dirs. Then tries to open it with system default action
view_channel_photo <channel>    Downloads file to downloads dirs. Then tries to open it with system default action
view_chat_photo <chat>  Downloads file to downloads dirs. Then tries to open it with system default action
view_document <msg-id>  Downloads file to downloads dirs. Then tries to open it with system default action
view_document_thumb <msg-id>    Downloads file to downloads dirs. Then tries to open it with system default action
view_file <msg-id>      Downloads file to downloads dirs. Then tries to open it with system default action
view_file_thumb <msg-id>        Downloads file to downloads dirs. Then tries to open it with system default action
view_photo <msg-id>     Downloads file to downloads dirs. Then tries to open it with system default action
view_user_photo <user>  Downloads file to downloads dirs. Then tries to open it with system default action
view_video <msg-id>     Downloads file to downloads dirs. Then tries to open it with system default action
view_video_thumb <msg-id>       Downloads file to downloads dirs. Then tries to open it with system default action
view <msg-id>   Tries to view message contents
visualize_key <secret chat>     Prints visualization of encryption key (first 16 bytes sha1 of it in fact)
[0mAll done. Exit
halt

大抵のことは出来る感じです.

試した環境1
$ git log --decorate|head -7
commit 6547c0b21b977b327b3c5e8142963f4bc246187a (HEAD -> master, origin/master, origin/HEAD)
Merge: 443793d 160231b
Author: V V <vvaltman@aurum>
Date:   Wed Mar 23 14:42:53 2016 +0300
Merge github.com:vysheng/tg
$ dpkg-query -W libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev libjansson-dev libpython-dev make libssl1.0-dev
libconfig-dev:armhf     1.5-0.3
libevent-dev    2.0.21-stable-3
libjansson-dev:armhf    2.9-1
liblua5.2-dev:armhf     5.2.4-1.1
libpython-dev:armhf     2.7.13-2
libreadline-dev:armhf   7.0-3
libssl-dev
libssl1.0-dev:armhf     1.0.2l-2+deb9u3
lua5.2  5.2.4-1.1
make    4.1-9.1
$ lsb_release -d
Description:    Raspbian GNU/Linux 9.4 (stretch)
$ uname -m
armv7l
$ cat /proc/device-tree/model ;echo
Raspberry Pi 2 Model B Rev 1.1
試した環境2
$ git log --decorate|head -7
commit 6547c0b21b977b327b3c5e8142963f4bc246187a (HEAD -> master, origin/master, origin/HEAD)
Merge: 443793d 160231b
Author: V V <vvaltman@aurum>
Date:   Wed Mar 23 14:42:53 2016 +0300
Merge github.com:vysheng/tg
$ dpkg-query -W libreadline-dev libconfig-dev libssl-dev lua5.2 liblua5.2-dev libevent-dev libjansson-dev libpython-dev make libssl1.0-dev
libconfig-dev:amd64     1.5-0.4
libevent-dev    2.1.8-stable-4build1
libjansson-dev:amd64    2.11-1
liblua5.2-dev:amd64     5.2.4-1.1build1
libpython-dev:amd64     2.7.15~rc1-1
libreadline-dev:amd64   7.0-3
libssl-dev
libssl1.0-dev:amd64     1.0.2n-1ubuntu5.1
lua5.2  5.2.4-1.1build1
make    4.1-9.1ubuntu1
$ lsb_release -d
Description:    Ubuntu 18.04.1 LTS
(mk)-(jobs:0)-(~/src/tg)
$ uname -m
x86_64

デーモンの起動しないSMTPクライアントの msmtp を試す(sSMTP乗り換え)

sSMTP から msmtp へ

現在メールを受け取らずメールを送信するだけのホストではsSMTPをよく使っています.MTAはeximやPostfix等でも良いのですが,デーモンが起動しないのでその分セキュアで設定も楽です.
しかし,sSMTPは最近メンテされていません,Debian などのディストリビューションではまだメンテナンスされていますが新機能が入ったりはしないでしょう.

ssmtp is unmaintained. Consider using something like msmtp instead.
バージョンも据え置きでUpstreamも無さそう
$ w3m -dump https://packages.qa.debian.org/s/ssmtp.html|grep versions -A11
versions ... ...pool
o-o-stable
save 2.64-7
oldstable
save 2.64-8
stable
save 2.64-8
unstable
save 2.64-8
Ubuntu
2.64-8ubuntu2
$ apt show ssmtp 2>/dev/null |grep Homepage
Homepage: http://packages.qa.debian.org/s/ssmtp.html

そこで,alternativeto.net で挙げられている msmtp を試してみました.

以下では色々試していますが,システムメールを送るだけなら msmtp, msmtp-mta pkg を導入,/etc/msmtprc の設定だけでokです.

msmtp の導入

Debian sid/Ubuntu 18.04 LTS などではパッケージがあったのでこれを導入しました.

$ sudo apt install msmtp

mstpの設定

設定例が /usr/share/doc/msmtp/examples/ 以下にあるので
システム設定の場合は msmtprc-system.example`/etc/msmtprc にcpして設定.
ユーザ設定の場合は msmtprc-user.example~/.msmtprc にcpして設定する.

システムでの設定例

以下の例では root が読み書きできて,一般ユーザのmatokenアカウントは読み込みが可能な設定ファイル /etc/msmtprc を作成している.
defaults セクションに共通設定を書き,account セクションにメールサーバ固有の設定を書く.account セクションは複数書けて,account default に規定account を指定する.以下の例ではgmail が規定値となる.

/etc/msmtprc の作成
$ sudo install /dev/null /etc/msmtprc -o root -g root -m 600
$ sudo setfacl -m u:matoken:r /etc/msmtprc
$ getfacl /etc/msmtprc
getfacl: Removing leading '/' from absolute path names
# file: etc/msmtprc
# owner: root
# group: root
user::rw-
user:matoken:r--
group::---
mask::r--
other::---
$ sudo vi /etc/msmtprc
$ sudo grep -v ^# /etc/msmtprc
defaults
syslog on
aliases /etc/aliases
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
auth on
syslog LOG_MAIL
account gmail
host smtp.gmail.com
port 587
from example@gmail.com
user example
password porseypacdoadwif
account default : gmail

※gmailの多要素認証を利用している場合は以下からアプリパスワードを生成する.(使い回しができちゃうけれど流出したときの影響を考えて設定毎に発行する)

ユーザでの設定

ユーザの設定ファイルは`~/.msmtprc` 若しくは $XDG_CONFIG_HOME/msmtp/config でシステムと違い,alias file と log を適当なファイルにした.
アカウントも増やしてみた.
gmailのアプリパスワードも新たに発行した.

~/.msmtprc の作成
$ install /dev/null ~/.msmtprc -o matoken -g matoken -m 600
$ vi ~/.msmtprc
$ grep -v ^# ~/.msmtprc
defaults
logfile ~/.msmtp.log
aliases ~/.msmtp.aliases
tls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
auth on
syslog LOG_MAIL
account gmail
host smtp.gmail.com
port 587
from example@gmail.com
user example
password igeinleedafchygy
account yandex
tls_starttls off
host smtp.yandex.com
port 465
from example@yandex.com
user example
password throjfeekdewxeib
account nifty
tls_starttls off
host smtp.nifty.com
port 465
from example@nifty.com
user aaa99999
password 9Z#9999X
account default : gmail

送信テスト

msmtp コマンドで送信テストが出来る.
-a オプションでアカウントが指定できる.
-C オプションで設定ファイルが指定できる.(システムとユーザの設定を両方テストする)

$ echo "hello system msmtp" | msmtp -C /etc/msmtprc matohara@gmail.com
$ echo "hello user msmtp" | msmtp -C ~/.msmtprc -a gmail matohara@gmail.com

大抵のMTAでは . でmail body 終了となるが,msmtpでは`.`は普通にbody に含まれる.Ctrl + d で終了.

$ msmtp -a gmail matohara@gmail.com
Subject: hello
msmtp test
.
dot では終了にならない
Ctrl+d で終了
^d

aliases file

msmtp設定ファイルの alias で設定できる.

$ grep alias ~/.msmtprc
aliases ~/.msmtp.alias
$ sudo grep alias /etc/msmtprc
aliases /etc/aliases

このalias fileは通常のaliases ファイルと同じ形式の plain text で newaliases コマンドでの変換は不要.

メールアドレスは複数指定できる.default という名前は規定アドレスとなる.

root: hoge@example.com, fuga@example.com
default: piyo@example.com

以前のaliases ファイルを流用して以下のようにしてみたところ,

# /etc/aliases
mailer-daemon: postmaster
postmaster: root
nobody: root
hostmaster: root
usenet: root
news: root
webmaster: root
www: root
ftp: root
abuse: root
noc: root
security: root
root: matohara@gmail.com
default: matohara@gmail.com

以下のようにエラーとなった.alias先はユーザ名ではエラーとなるよう.

$ echo hello | sudo msmtp -C /etc/msmtprc root
msmtp: /etc/aliases: line 2: invalid address 'postmaster'

以下のようにそれぞれにメールアドレスを指定すると期待した動作となった.

$ sudo sed -i -e 's/ root$\| postmaster$/matohara@gmail.com/' /etc/aliases
$ cat /etc/aliases
# /etc/aliases
mailer-daemon:matohara@gmail.com
postmaster:matohara@gmail.com
nobody:matohara@gmail.com
hostmaster:matohara@gmail.com
usenet:matohara@gmail.com
news:matohara@gmail.com
webmaster:matohara@gmail.com
www:matohara@gmail.com
ftp:matohara@gmail.com
abuse:matohara@gmail.com
noc:matohara@gmail.com
security:matohara@gmail.com
root: matohara@gmail.com
default: matohara@gmail.com

パスワードをGnuPG暗号化ファイルに格納して利用する

パスワードをgpgで暗号化したファイルに格納することが可能.この場合アカウントごとにファイルが必要なのが少し面倒.
password porseypacdoadwif の場合 porseypacdoadwif 部分だけを暗号化してファイルに格納する.(はじめpassword も含めてしまい失敗した)

$ grep ^password\  ~/.msmtprc | head -1 | awk {'print $2'} | gpg2 --default-recipient-self -e - > ~/.msmtp-password-gmail.gpg
$ grep ^password\  ~/.msmtprc | head -2 | tail -1 | awk {'print $2'} | gpg2 --default-recipient-self -e - > ~/.msmtp-password-yandex.gpg
$ grep ^password\  ~/.msmtprc | tail -1 | awk {'print $2'} | gpg2 --default-recipient-self -e - > ~/.msmtp-password-nifty.gpg

msmtprc の password を削除して,代わりに passwordeval gpg2 --no-tty -q -d 暗号ファイル と設定を変更する.

$ grep ^passworde ~/.msmtprc
passwordeval gpg2 --no-tty -q -d ~/.msmtp-password-gmail.gpg
passwordeval gpg2 --no-tty -q -d ~/.msmtp-password-yandex.gpg
passwordeval gpg2 --no-tty -q -d ~/.msmtp-password-nifty.gpg

後はこれまでのようにmsmtpを呼ぶとgpg-agentが起動する.

※システム側はシステムメールが送れなくなるのでこの設定をしないほうがいいと思う.

パスワードをopenssl暗号化ファイルに格納

システムメールでgpg agentを利用するのは多分無理なのでopensslで暗号化してみる.パスワードはNICのmac addressにしてある.気休めレベルだけど試しに設定.
マシンが乗っ取られた場合はどうしようもないけれど /etc/msmtprc, /etc/msmtp-password ファイル流出時の時間稼ぎくらいにはなる?
NIC が使っていないものも含め増減したり変更になった場合は復元できなくなるので注意.

mac address をパスワードとしてopensslで暗号化
$ umask 0077; grep ^password\  /etc/msmtprc | head -1 | awk {'print $2'} | sudo openssl enc -e -md sha256 -aes-256-cbc -a -A -salt -pbkdf2 -in /dev/stdin -out /etc/msmtp-password -pass pass:`hwinfo --network | grep 'Permanent HW Address' | sed 's/^.*: //' | sort -n | tr -d '\n'`
復元テスト
$ sudo openssl enc -d -md sha256 -aes-256-cbc -a -A -salt -pbkdf2 -in /etc/msmtp-password -pass pass:`hwinfo --network | grep 'Permanent HW Address' | sed 's/^.*: //' | sort -n | tr -d '\n'`
igeinleedafchygy
/etc/msmtprc の password を削除して passwordeval に書き換え
$ grep passworde /etc/msmtprc
passwordeval openssl enc -d -md sha256 -aes-256-cbc -a -A -salt -pbkdf2 -in /etc/msmtp-password -pass pass:`hwinfo --network | grep 'Permanent HW Address' | sed 's/^.*: //' | sort -n | tr -d '\n'`

※ifconfigやiproute2を使わずhwinfoを使ってPermanent HW Addressを使っているのはmac address偽装対策

MTA パッケージの導入

msmtp コマンドでメールが送信できるようになったら,msmtp-mta pkg を導入する.これでsendmailコマンドにシンボリックリンクが張られて同様に利用できるようになる.システムメールも飛ぶようになる.

$ sudo apt install msmtp-mta
$ ls -l /usr/sbin/sendmail
lrwxrwxrwx 1 root root 12 11月 29  2016 /usr/sbin/sendmail -> ../bin/msmtp

オフライン時にmsmtpを利用する

msmtpはオフライン時に実行するとエラーとなる.通常は常にオンラインなので問題はないが,持ち運びNotePCなどではオフラインオンラインが発生してメールの送信に失敗することがある.

$ echo 'hoge' | msmtp -a gmail matoken@gmail.com
msmtp: cannot locate host smtp.gmail.com: 名前またはサービスが不明です
msmtp: could not send mail (account gmail from /etc/msmtprc)

/usr/share/doc/msmtp/examples/ 以下の msmtpq 若しくは msmtpqueue に格納されているscriptを利用することでオフライン時にキューとして貯めておいてオンライン時に送信といったことが出来る.

どちらもsendmailと置き換えが可能なので,ネットワークダウン時に`/usr/sbin/sendmail`をこれに置き換えてオンライン時に戻してqueueをフラッシュするようにするといいかもしれない.

Debian/Ubuntuなら以下のあたりで,

  • /etc/network/if-down.d/
    /usr/sbin/sendmail を msmtpq or msmtpqueue に向ける

  • /etc/network/if-pre-up.d/
    /usr/sbin/sendmail を msmtp に向ける

  • /etc/network/if-up.d/
    queueをフラッシュ

NetworkManagerだと /etc/NetworkManager/dispatcher.d/ 以下で同じことが出来る.

msmtpq

圧縮されているmsmtpqを展開して実行権を付与する
$ sudo gunzip /usr/share/doc/msmtp/examples/msmtpq/msmtpq.gz
$ sudo chmod 755 /usr/share/doc/msmtp/examples/msmtpq/msmtpq
使いやすい場所にシンボリックリンクを張る
$ sudo ln -s /usr/share/doc/msmtp/examples/msmtpq/msmtpq /usr/local/bin/msmtpq
$ sudo ln -s /usr/share/doc/msmtp/examples/msmtpq/msmtp-queue /usr/local/bin/msmtp-queue
queueとlogディレクトリを作成
$ install -d -m 700 ~/.msmtp.queue
$ install -d -m 700 ~/log
オフライン時に送信しようとする
$ echo 'msmtpq' | msmtpq -C ~/.msmtprc -a gmail matoken@gmail.com
mail for [ -C /home/matoken/.msmtprc -a gmail matoken@gmail.com ] : couldn't be sent - host not connected
enqueued mail as : [ 2018-09-18-06.21.48 ] ( -C /home/matoken/.msmtprc -a gmail matoken@gmail.com ) : successful
queueを確認する
$ msmtp-queue -d
mail  num=[ 1 ]  id=[ 2018-09-18-06.21.18 ]
mail  num=[ 2 ]  id=[ 2018-09-18-06.21.48 ]
queueをフラッシュする
$ msmtp-queue -r
mail [ 1 ] [ 2018-09-18-06.21.18 ] from queue ; send was successful ; purged from queue
mail [ 2 ] [ 2018-09-18-06.21.48 ] from queue ; send was successful ; purged from queue
$ msmtp-queue -d
no mail in queue

msmtpqueue

使いやすい場所にシンボリックリンクを張る
$ sudo ln -s /usr/share/doc/msmtp/examples/msmtpqueue/msmtp-enqueue.sh /usr/local/bin/msmtp-enqueue.sh
$ sudo ln -s /usr/share/doc/msmtp/examples/msmtpqueue/msmtp-listqueue.sh /usr/local/bin/msmtp-listqueue.sh
~$ sudo ln -s /usr/share/doc/msmtp/examples/msmtpqueue/msmtp-runqueue.sh /usr/local/bin/msmtp-runqueue.sh
`msmtp`コマンドの代わりに`msmtp-enqueue.sh`を使う
$ echo 'queue' | msmtp-enqueue.sh -C ~/.msmtprc -a gmail matoken@gmail.com
queueを確認する
$ msmtp-listqueue.sh
No mail in queue
今回のようにメールの内容がbodyだけでFrom, To, Subject が存在しないとこのscriptはうまく機能しない><
         egrep -s --colour -h '(^From:|^To:|^Subject:)' "$i" || echo "No mail in queue";
直接`~/.msmtpqueue/`を確認するとqueueが溜まっているのがわかる
$ ls -lA ~/.msmtpqueue/
合計 8
-rw------- 1 matoken matoken  7  9月 18 00:15 2018-09-18-00.15.53.mail
-rw------- 1 matoken matoken 53  9月 18 00:15 2018-09-18-00.15.53.msmtp
Subjectの付いたメールがある時
$ msmtp-listqueue.sh
No mail in queue
Subject: offline><
オンライン時にqueueをフラッシュする
$ msmtp-runqueue.sh
*** Sending 2018-09-18-00.15.53.mail to -C /home/matoken/.msmtprc -a gmail matoken@gmail.com ...
2018-09-18-00.15.53.mail sent successfully
*** Sending 2018-09-18-00.27.21.mail to -C /home/matoken/.msmtprc -a gmail matoken@gmail.com ...
2018-09-18-00.27.21.mail sent successfully

#sudoの場合も ~root/.msmtpqueue/ だった.

tips

gmailで以下のエラーとなった場合 tls_starttls on ( tls onのときの既定値? )と設定すると送信できた
$ echo "hello msmtp" | sudo msmtp -a gmail matohara@gmail.com
msmtp: envelope from address matohara@gmail.com not accepted by the server
msmtp: server message: 530 5.7.0 Must issue a STARTTLS command first. k126-v6sm14991126pgk.26 - gsmtp
msmtp: could not send mail (account gmail from /etc/msmtprc)
Yandexでメール送信時に帰ってこない場合 tls_starttls off と設定すると送信できた

niftyで以下のエラーとなった場合 tls_starttls off と設定すると送信できた
msmtp: network read error: 接続が相手からリセットされました
msmtp: could not send mail (account nifty from /etc/msmtprc)

試した環境

環境1(Raspberry Pi 2B/Raspbian stretch)
$ dpkg-query -W msmtp msmtp-mta gnupg2 openssl
gnupg2  2.1.18-8~deb9u2
msmtp   1.6.6-1
msmtp-mta       1.6.6-1
openssl 1.1.0f-3+deb9u2
$ lsb_release -d
Description:    Raspbian GNU/Linux 9.4 (stretch)
$ uname -m
armv7l
環境2(Ubuntu 18.04 LTS)
$ dpkg-query -W msmtp msmtp-mta gnupg2 openssl
gnupg2  2.2.4-1ubuntu1.1
msmtp   1.6.6-1
msmtp-mta       1.6.6-1
openssl 1.1.0g-2ubuntu4.1
$ lsb_release -d
Description:    Ubuntu 18.04.1 LTS
$ uname -m
x86_64
環境3(Debian sid)
$ dpkg-query -W msmtp msmtp-mta gnupg2 openssl
gnupg2  2.2.10-1
msmtp   1.6.7-1
msmtp-mta
openssl 1.1.1-1
$ lsb_release -d
Description:    Debian GNU/Linux unstable (sid)
$ uname -m
x86_64

crontab から送信される電子メールの件名を指定したい

crontab で実行されたコマンドの出力はメールで送られますが,このときの件名は「Cron <ユーザ名@ホスト名> 実行コマンド」のようになります.

crontab 例
15 7 * * *  echo "cron subject"
メール例
Subject: Cron <matoken@T430s> echo "cron subject"
cron subject
1つのhostからこんなメール飛んで来るとどれがどれかわかりづらい
Cron <matoken@T430s> nice -n 19 ionice -c 3 rsync -avxze "ssh -i……
Cron <root@T430s> nice -n 19 ionice -c 3 rsync -avxze "ssh -i……
Cron <root@T430s> nice -n 19 ionice -c 3 rsync -avxze "ssh -i……

分かりづらいので自分で件名を付けたいところです.

: を利用する

: は shell の組み込み関数で何もせず正常終了します.
コマンドなので # と違い,; の後にコマンドを書くとその後のコマンドは解釈されます.

$ :           #何もしない
$ : hoge
$ : echo hoge
$ : rm -rf *
$ : $(hostname)  #コマンドも展開せず何もしない
$ : `whoami`
$ : hoge; echo fuga   #;の後は解釈される
fuga
$ # hoge
$ # hoge; echo fuga

これをcrontabの頭に書くとこういう感じになります.

crontab
29 6 * * *  : cron subject; echo "hello"
mail
Subject: Cron <matoken@T430s> : cron subject; echo "hello"
hello

少しわかりやすくなりました.

mailコマンドを利用する

これはちょっと反則な感じがするのですが件名にコマンド実行結果を入れたり,頭の部分も書き換えたい場合はcronだけではできなさそうなのでmailコマンド経由で送信してみます.今回mailutilsのmailコマンドを利用しましたが,bsd-mailxやmutt等々ももちろん使えます.

crontab
45 6 * * *  echo "cron test" | mail -s "$(hostname)@$(whoami) cron Subject : $(date +\%F)" matoken+cron@example.com
mail
Subject: T430s@matoken cron Subject : 2018-09-13
X-Mailer: mail (GNU Mailutils 3.4)
cron test

mail コマンドの -s オプション部分が件名になります.その後ろが宛先のメールアドレス.bodyはcronの出力をパイプから受け取ります.

STDERR が別メールで届く

STDERR の出力があった場合cronでメールが送られてしまいます.mailコマンドと合わせて1度に2通届くことに.
実行コマンドの後ろに 2>&1 を付けて STDERR を STDOUT に渡してmailコマンドだけにします.

crontab
45 6 * * *  echo "cron test" 2>&1 | mail -s "$(hostname)@$(whoami) cron Subject : $(date +\%F)" matoken+cron@example.com

出力がなくてもメールが届く

crontab の場合コマンドの出力がなければメールが送られませんが,このコマンドでは NULL でもメールが送信されます.
未解決.

余録

cronで一切メールを送りたくない

場合crontabで MAILTO=”” を設定する
MAILTO=""

指定したコマンドだけメールを送りたくない

STDOUT と STDERR を /dev/null に捨てる
20 6 * * *  echo "cron subject" > /dev/null 2>&1

メールが届かない

/var/spool/mail/ユーザ名 のローカルにはメールがどとくけど,インターネット上のメールアドレスを指定しても届かない場合メールサーバの設定が出来ていないかもしれません.メールサーバ(sSMTP/msmtp等はデーモンが起動しないのでメールを受け取らず送信するだけならおすすめ)を設定してから再度試してみましょう.

環境1
$ man cron | grep -m1 cron
cron - daemon to execute scheduled commands (Vixie Cron)
$ dpkg-query -W cron mailutils
cron    3.0pl1-128.1ubuntu1
mailutils       1:3.4-1
$ lsb_release -d
Description:    Ubuntu 18.04.1 LTS
$ uname -m
x86_64
環境2
$ man cron | grep -m1 cron
cron - 予定されたコマンドを実行するデーモン(Vixie Cron)
$ dpkg-query -W cron mailutils
cron    3.0pl1-130
mailutils       1:3.4-1+b1
$ lsb_release -d
Description:    Debian GNU/Linux unstable (sid)
$ uname -m
x86_64

Debian stretch で php5 / php7.0 を切り替え

update-alternatives で OK かなと思ったのですが,

$ sudo update-alternatives --config php
There are 2 choices for the alternative php (providing /usr/bin/php).
Selection    Path             Priority   Status
--------------------
* 0            /usr/bin/php7.0   70        auto mode
1            /usr/bin/php5     50        manual mode
2            /usr/bin/php7.0   70        manual mode
Press <enter> to keep the current choice[*], or type selection number: 0
$ php -v
PHP 7.0.30-0+deb9u1 (cli) (built: Jun 14 2018 13:50:25) ( NTS )
Copyright (c) 1997-2017 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies
with Zend OPcache v7.0.30-0+deb9u1, Copyright (c) 1999-2017, by Zend Technologies

apache では php5 のままです.

$ w3m -dump http://localhost/phpinfo.php|grep -m1 'PHP Version'
PHP Version 5.6.30-0+deb8u1

apache module を切り替えたらokでした.

$ sudo a2dismod php5
$ sudo a2enmod php7.0
$ systemctl restart apache2
環境
$ dpkg-query -W apache2 php5 libapache2-mod-php5 php7.0 libapache2-mod-php7.0
apache2 2.4.25-3+deb9u5
libapache2-mod-php5     5.6.30+dfsg-0+deb8u1
libapache2-mod-php7.0   7.0.30-0+deb9u1
php5    5.6.30+dfsg-0+deb8u1
php7.0  7.0.30-0+deb9u1
$ lsb_release -d
Description:    Debian GNU/Linux 9.5 (stretch)
$ uname -m
x86_64

trash-cli で cli でもゴミ箱利用

rm コマンドはとても強力です.バックアップもなく誤って削除したファイルは復旧できない可能性が高いです.
そこで rm の alias に rm -i を指定して確認メッセージを出すなどしている環境もありますが,いつも使っていると手癖で即削除してしまったりして確実ではありません.GUI環境のファイラーなどではゴミ箱機能が使えて削除してもゴミ箱を空にするまで復元が可能です.
これをcuiでも利用できる trash-cli というツールがあります.デスクトップと共通(freedesktop.org trashcanと共通)のゴミ箱が使えて便利です.

削除してしまったファイルを復元したい場合なるべく早く削除したファイルシステムを umount or mount -o ro してファイル復元ツールを試してみるともしかしたら復元できるかもしれません.Debian の場合 forensics-all/forensics-extra pkg 等を覗いてみると様々なツールが見つかると思います.(例えばext3/ext4ファイルシステムでの復元ツールのextundeleteや汎用のscalpel等)

導入
$ sudo apt install trash-cli
$ dpkg -L trash-cli|grep bin/
/usr/bin/restore-trash
/usr/bin/trash
/usr/bin/trash-empty
/usr/bin/trash-list
/usr/bin/trash-put
/usr/bin/trash-rm
trash ゴミ箱に捨てる
$ touch hoge  #ファイル作成
$ trash hoge  #ファイルを捨てる
$ mkdir -p hoge/fuga/piyo #ディレクトリ作成
$ trash hoge  #ディレクトリを捨てる
trash-list ゴミ箱の中を見る(同じ場所同じ名前のファイルもそれぞれ管理される)
$ trash-list | grep hoge
2018-09-11 05:36:19 /home/matoken/tmp/hoge
2018-09-12 08:57:31 /home/matoken/tmp/hoge
2018-09-12 08:37:09 /home/matoken/tmp/hoge
2018-09-12 08:57:35 /home/matoken/tmp/hoge
2018-09-12 08:36:56 /home/matoken/tmp/hoge
2018-09-11 05:36:34 /home/matoken/tmp/hoge
trash-empty ゴミ箱を空にする
$ trash-empty
$ trash-list
restore-trash 対話的にファイルを復元する
$ restore-trash
0 2018-09-14 05:15:11 /home/matoken/tmp/piyo
1 2018-09-14 05:13:22 /home/matoken/tmp/hoge
2 2018-09-14 05:15:11 /home/matoken/tmp/fuga
What file to restore [0..2]: 0  #復元したいファイルの番号を指定
trash を rm の alias にする(alias にてオプションを付与してオプションは無視するようになっている)
$ alias rm='trash'
$ rm hoge
$ rm -rf dir

という感じで簡単に使えます.sudo を使うことも可能です.ゴミ箱の実体は環境によりますが,手元の環境では ~/.local/share/Trash/ 以下でした.このあたりは以下のページが参考になると思います.

ゴミ箱を空にしないとどんどんファイルは溜まっていきます.autotrash を使って掃除するのがおすすめです.

<追記>

Google+のコメントでmasakaさんに教えてもらったのですが,trash-empty 日付 と日付を指定することで日付以上の日数が経過したファイルが削除できるようです.日付指定だけならautotrash を導入しなくてもいいですね.

Masakazu Takahashi

ちなみにtrash-cliでも`trash-empty 30`とか実行すると、削除して30日超のファイルをゴミ箱から消してくれます。

man trash-emptyより
ARGUMENTS
To remove all trashed files, use 'emtpy-trash'.
To remove files that have been in the trash more than a given number of
days, use 'trash-empty x', 'x' representing the number of days.

</追記>

環境
$ dpkg-query -W trash-cli
trash-cli       0.12.9.14-2.1
$ lsb_release -d
Description:    Debian GNU/Linux unstable (sid)
$ uname -m
x86_64

#trash-cli は現在 0.17.1.14 で 0.12.9.14 のリリース日は 15 Sep 2012 となっている.