この記事は 2014 年に「Linux でメール(mail)を確認する」という題で公開されましたが、本文にはローカルメールボックスの閲覧、テスト送信、複数ユーザー間の端末メッセージが混在していました。現在の版では問いを一つに絞り、Linux のコマンドラインから許可されたメールを 1 通、安全かつ監査可能な形で送る方法を扱います。ローカルメールの閲覧、IMAP/POP3 の受信、write による端末通信は別の機能であり、コマンドを相互に代用できません。
境界を先に確認します。本稿を使えるのは、自分が所有するか利用許可を得た送信アカウント、ドメイン、ホスト、受信先だけです。オープンリレー、送信者詐称、迷惑行為対策の回避、未承諾の一括送信、送信元の隠蔽は扱いません。「まず動かす」ために TLS 証明書検証を無効化したり、パスワードを引数や URL に置いたり、同意のない宛先へ本番キューを向けたりしてはいけません。
末尾には
source_exportの可視本文を完全保存しています。アーカイブ内の古いメールアドレスとメッセージ識別子だけを限定的に伏せ、古い HTTP リンクは現在の推奨ではなく来歴として残しました。
Table of Contents
まず正しい送信経路を選ぶ
| 経路 | 適した用途 | 信頼境界 | 不適切な用途 |
|---|---|---|---|
| ローカル MTA の sendmail 互換インターフェース | 管理者管理のサーバー、システム通知、既存のキューとバウンス処理 | アプリがローカルの Postfix、Exim、Sendmail 等へ渡し、その MTA が配信 | 個人ホストでの即席公開 SMTP、組織方針の迂回 |
| 認証付き SMTP submission | プロバイダーが submission ホストを示す単一ユーザーのスクリプトやクライアント | クライアントが TLS を検証してプロバイダーへ認証し、以降を委任 | 任意の受信 MX の 25 番へ直接配信、長期パスワードの共有 |
| プロバイダーのメール API | 構造化レスポンス、冪等キー、テンプレート、イベント状態が必要なアプリ | 最小権限 API 資格情報とプロバイダーのデータ・再試行規則 | 架空の汎用エンドポイント、CLI 上のトークン、割当回避 |
mail/mailx はメールユーザーエージェント群の名前で、実装とオプションはディストリビューションにより異なります。sendmail も互換インターフェースにすぎない場合が多く、背後の MTA を特定しません。バイナリ名から設定を推測せず、システム所有者、パッケージ由来、責任者を確認します。
変更前にプロバイダーの事実を記録する
組織管理者またはプロバイダーの最新公式文書だけを使い、確認日とともに次を記録します。
- submission ホスト名と、587/STARTTLS または 465/暗黙 TLS の正確な組み合わせ。
- OAuth 2.0、短期トークン、アプリパスワードなど、組織が認める認証方式。
- 検証済み
From:、許可された envelope-from/Return-Path ドメイン、DKIM 署名の責任者。 - サイズ、速度、同時実行、日次割当、宛先数、再試行の制限。
- バウンス、苦情、抑止リスト、ログ、本文の保存場所と期間。
- API 利用時の地域、版、スコープ、冪等性、失効手順。
通常のログインパスワードが SMTP AUTH で使えるとは限らず、多要素認証を有効にしても再利用可能とは限りません。OAuth やアプリパスワードの可否はプロバイダーと組織方針次第です。公式パラメーター、送信権限、管理下のテスト受信箱のいずれかがなければ停止します。
エンベロープとメッセージヘッダーを分ける
SMTP エンベロープと読者に見えるヘッダーは別の層です。RFC 5321 は転送エンベロープ、RFC 5322 はメッセージ形式を定義し、両者が自動的に一致する保証はありません。
| フィールド | 用途 | セキュリティ要件 |
|---|---|---|
| envelope MAIL FROM | バウンス先。SPF/DMARC がそのドメインを使う場合がある | プロバイダー承認済みでバウンス処理可能な識別子を使い、スクリプトで捏造しない |
| envelope RCPT TO | 実際の配信先 | 固定許可リストまたは検証済み業務データだけを受け入れ、改行注入を拒否 |
From: | 読者に見える著者、DMARC Author Domain の出所 | 許可済みで、プロバイダーの SPF/DKIM 設計に整合するものを使う |
To: / Cc: | 表示上の受信者 | エンベロープ宛先と同じとは限らない。他人のアドレスを漏らさない |
Bcc: | エンベロープでは配信し、完成ヘッダーからは除去 | クライアントが削除することを確認。一括配信は大きな Bcc でなく適法なリストシステムを使う |
Message-ID: / Date: | 重複排除、スレッド、診断 | 信頼するクライアント/MTA に生成させ、ログ共有前に脱敏 |
信頼できない文字列をヘッダーへ連結してはいけません。CR/LF で追加ヘッダーや宛先を注入できます。例は RFC 予約済みの .invalid ドメインを使うため、そのままでは配信できません。
経路 1:既存ローカル MTA の sendmail インターフェース
まず読み取り専用で棚卸しします。
command -v sendmail
command -v msmtp
msmtp --version
/usr/sbin/sendmail があれば、所有する保守中パッケージ、管理者、待受インターフェース、認証/中継制限、キューとバウンスの担当を確認します。確認済みメッセージをローカルキューへ渡す互換呼び出しは一般に次の形です。
/usr/sbin/sendmail -t -oi < "$HOME/mail-test.eml"
-t は通常ヘッダーから宛先を読み、-oi は点だけの本文行による早期終了を防ぎますが、最終的な意味は導入済み実装の手引きで確認してください。終了成功はローカル MTA が受理したという意味で、遠隔配信の証明ではありません。インターネットへ待受を公開する、認証なし中継を作る、mynetworks/ACL を広げる、-f でドメインを詐称することは禁止です。管理者承認済みのキュー、バウンス、ログ処理がなければ組織の submission/API を使います。
経路 2:認証付き SMTP submission
RFC 6409 はメッセージ submission とサーバー間中継を分けています。通常のクライアントは、受信 MX の 25 番へ直接送らずプロバイダーの submission を使います。RFC 8314 は二つの安全な構成を認めます。
| モード | 一般的ポート | TLS 開始時点 | 必須の検証 |
|---|---|---|---|
| STARTTLS submission | 587 | SMTP 開始後 STARTTLS で昇格。失敗時は停止 | ホスト名、証明書チェーン、有効期間、STARTTLS 成功、その後にだけ認証 |
| 暗黙 TLS submission | 465 | TCP 接続直後から TLS | 認証前にホスト名、証明書チェーン、有効期間 |
ポートは慣例であり、事実は公式設定です。システム時刻や CA ストアの問題でも失敗します。原因を直し、trust-all、tls_certcheck off、ホスト名無視、未確認の証明書指紋固定は使いません。
資格情報や本文を送らず、公式ホスト名で TLS を調べられます。まずローカル OpenSSL が各オプションに対応するか確認してください。
openssl s_client -starttls smtp -connect smtp.example.invalid:587 -servername smtp.example.invalid -verify_hostname smtp.example.invalid -verify_return_error -brief </dev/null
openssl s_client -connect smtp.example.invalid:465 -servername smtp.example.invalid -verify_hostname smtp.example.invalid -verify_return_error -brief </dev/null
成功しても、その時点のネットワーク/TLS 経路だけの証拠で、アカウント認証、送信者権限、最終配信は証明しません。ホスト名不一致、未知の CA、ダウングレード/キャプティブプロキシ、587 で STARTTLS がない、予期しないホストへの接続では停止します。
非秘密設定を分離した msmtp 設定に置く
msmtp は sendmail 互換インターフェースを持つ保守中の SMTP クライアントです。対応中のディストリビューションパッケージから導入し版を確認してから、既定アカウントを上書きしない分離ファイルを作ります。
ConfigDir="$HOME/.config/msmtp"
ConfigFile="$ConfigDir/config-controlled"
test ! -L "$ConfigDir" || { echo "Stop: $ConfigDir is a symlink"; exit 1; }
test ! -e "$ConfigFile" && test ! -L "$ConfigFile" || { echo "Stop: $ConfigFile already exists"; exit 1; }
umask 077
install -d -m 700 -- "$ConfigDir"
install -m 600 /dev/null "$ConfigFile"
ディレクトリとファイルが現在のユーザー所有であることを確認します。エディターで次の 587/STARTTLS テンプレートを入力します。全値は配信不能なプレースホルダーのままです。
account controlled
host smtp.example.invalid
port 587
auth on
user sender@example.invalid
from sender@example.invalid
allow_from_override off
tls on
tls_starttls on
tls_trust_file system
set_from_header on
set_date_header auto
set_msgid_header auto
remove_bcc_headers on
このテンプレートは msmtp 1.8.34 公式手引きを対象とします。導入版の公式手引きに set_msgid_header がなければ、推測やエラー無視はせず、対応パッケージ経由で更新するか、確認済み MUA/プロバイダーに Message-ID: を生成させます。
プロバイダーが 465/暗黙 TLS を明示した場合に限り、次の 2 行へ変更します。
port 465
tls_starttls off
msmtp では tls_starttls off と tls on の組み合わせは接続開始時から TLS トンネル内という意味で、暗号化の無効化ではありません。既定の証明書検証を維持し、tls_certcheck off を追加しません。
設定には意図的に password がありません。対話テストでは対応システムキーリングか安全な TTY プロンプトを使います。無人処理はプロバイダーと msmtp 公式文書に従い、監査済み OAuth トークン補助、キーリング、passwordeval を統合します。msmtp は旧 ~/.netrc を探す場合もあるため、そのファイルを表示せず、テストアカウントに意図しない一致がないことを確認します。補助コマンドは最小権限とし、期待値以外の stdout、ログ、環境スナップショット、一時ファイルへ秘密を出してはいけません。パスワード/token を argv、URL、Shell 履歴、リポジトリ、.netrc 例、スクリーンショットへ置くのは禁止です。
送信せず有効設定を展開します。--pretend はパスワードをアスタリスク表示にしますが、識別子、ホスト、パスは残り得るため共有前に脱敏します。
msmtp --file="$HOME/.config/msmtp/config-controlled" --account=controlled --pretend
実アカウントで --debug を使わないでください。公式手引きは完全な会話に容易に復号可能なパスワード情報が出る可能性を警告しています。
最小メッセージを作り、先に管理下の sink へ送る
以下を権限 600 の $HOME/mail-test.eml として保存します。最初は .invalid のままとし、添付、個人情報、本番テンプレート、実 Bcc、追跡ピクセルを入れません。
From: sender@example.invalid
To: controlled-recipient@example.invalid
Subject: controlled submission test
Auto-Submitted: auto-generated
This is an authorized delivery test. No reply is required.
第 1 段階では、分離したテストアカウントを組織承認済みの開発 SMTP sink/サンドボックスへ向けます。sink はループバックまたは管理下テストネットワークだけに bind し、インターネットへ中継せず、保存期間を短くします。正しいヘッダーで 1 通だけ捕捉し、Bcc/秘密がないことを確認後、テストデータを消します。--pretend は使用予定設定の確認だけで、sink テストの代替ではありません。
第 2 段階で初めて、ホスト、送信者、唯一の宛先を自分が管理しプロバイダー承認済みの値へ置換します。差分を 1 行ずつ確認し、1 回だけ送ります。
msmtp --file="$HOME/.config/msmtp/config-controlled" --account=controlled --read-recipients < "$HOME/mail-test.eml"
この設定は envelope-from を固定し、ヘッダーからエンベロープ宛先を読みます。未検証アドレスを位置引数にせず、初回をループ、スケジューラー、再試行ワーカーから実行しません。対話的資格情報プロンプトは確認済みローカルクライアントからのものに限り、Web ポップアップや未知のスクリプトを信頼しません。
経路 3:プロバイダー API
API は構造化 submission ID、冪等性、イベントコールバックが必要なアプリに適しますが、メール方針を迂回する手段ではありません。最新公式 HTTPS エンドポイント、SDK、証明書検証だけを使います。token は OS 秘密ストレージか読み取り専用ランタイム mount に置き、1 送信サービス/ドメインと必要操作だけに絞り、ローテーションと失効を定義します。
「汎用 curl」例をコピーして Bearer token を argv や環境へ置かないでください。リクエスト本文、失敗レスポンス、プロキシ、APM も宛先や本文を記録し得ます。公式サンドボックス/検証モードから始め、本番初回は管理下の 1 宛先と固有の冪等キーに限定します。一時エラーだけを公式規則に従う上限付きバックオフで再試行し、恒久拒否、権限、割当エラーを盲目的に再送しません。
SPF・DKIM・DMARC はドメイン所有者が統括する
- SPF は envelope MAIL FROM/HELO ドメインを送信できる基盤を承認します。CLI クライアントがヘッダーを足して「SPF pass」にはできません。
- DKIM は信頼済み MTA/プロバイダーがドメイン鍵で追加します。秘密鍵を一般スクリプトやワークステーションへ配りません。
- DMARC は可視
From:の Author Domain と、pass した SPF または DKIM 識別子との alignment を要求します。現行 RFC 9989 は RFC 7489 を置換しました。pass はドメイン利用の承認であり、内容の安全や受信箱到達の保証ではありません。
DNS レコード、selector、報告先、適用方針を変更できるのはドメイン所有者か明示的委任先だけです。全合法送信フローを棚卸しし、プロバイダー検証を完了して報告を観測後、段階的に方針を変えます。他者の TXT レコードをコピーしたり、直ちに reject へ移行したりしません。DMARC 報告とバウンスにはアドレス、IP、ヘッダー、本文断片が含まれ得るため、アクセスと保存を制限します。
配信証拠、バウンス、プライバシー
| 証拠 | 証明できること | 証明できないこと |
|---|---|---|
| クライアント終了コード/submission ID | クライアントまたは submission/API が要求を受理 | 最終サーバー受理や受信箱到達 |
| ローカル queue ID | ローカル MTA がキューへ追加 | 最終成功や認証 pass |
遠隔 SMTP 250/provider delivered イベント | ある遠隔段階がメッセージを受理 | 開封、内容の信頼性、後発バウンスなし |
受信原文の Received、Authentication-Results | テストの実経路と SPF/DKIM/DMARC 結果 | 全プロバイダー、全将来メールの同一挙動 |
| DSN/バウンス/苦情イベント | 特定の失敗または利用者シグナル | 無限再試行や継続連絡の許可 |
記録するのはテスト時刻、設定版、submission/queue ID、脱敏済み状態コード、最終結果だけにします。本文全体、OAuth token、AUTH 会話、全宛先一覧、未脱敏ヘッダーを長期保存しません。バウンス先には担当者が必要です。ハードバウンス、苦情、配信停止、明示拒否は自動再送でなく抑止処理へ入れます。
4xx は通常一時状態、5xx は通常恒久拒否ですが、プロバイダー文書と拡張状態コードに従います。再試行には回数/時間上限、jitter、重複排除が必要です。元要求が受理されたか不明なら、重複を作る前に submission ID で照会します。
よくある障害と停止条件
| 症状 | 最初に見る証拠 | 次の行動または停止条件 |
|---|---|---|
| TLS ホスト名/チェーン/期限失敗 | ホスト名、時刻、CA 更新、プロキシ | 事実の原因を修正し、検証を無効化しない |
| 587 に STARTTLS がない | 公式ポート、EHLO capability、介入 | 認証停止。平文上で秘密を送らない |
535/認証失敗 | 認証種別、方針、token scope/期限 | 連続推測せず、露出した資格情報を失効して公式再発行 |
550 送信者/宛先拒否 | 検証済み識別子、エンベロープ/ヘッダー、拡張状態 | From: 詐称やドメイン巡回で回避しない |
| ローカル成功だがメールなし | 正確な queue ID、MTA ログ、バウンス経路 | 管理者が該当記録を調査し、全キューを乱暴に消さない |
| SPF/DKIM/DMARC 失敗 | 受信原文、DNS、署名ドメイン、alignment | ドメイン/プロバイダー担当が修正し、偽認証ヘッダーを作らない |
| 割当超過または多数の 4xx | 制限、キュー深度、重複キー | 生産側を停止し、並列度で制限回避しない |
| 宛先/内容の由来不明 | 権限、同意、データ由来、保存根拠 | 責任者が確認するまで送らない |
tls_certcheck off、--insecure/trust-all、平文 AUTH、パスワード引数、オープンリレー、送信元詐称、購入/収集リスト、未承諾の一括メール、プロバイダーの迷惑行為対策・停止・抑止機能の無効化はすべて禁止です。
ロールバック、失効、オフボーディング
- 初回本番テスト前に、脱敏済み設定差分、担当者、テスト先、速度上限、ロールバック時間を保存します。
- 異常時は新規メールを作るスケジューラー/キュー入口を先に止め、他アプリのキューを一括削除しません。
- 今回の正確な submission/queue ID だけを扱い、受理済みメールがなお届く可能性を確認します。
- 以前の検証済み設定を戻し、専用 token/アプリパスワードを失効または交換し、一時メッセージと sink データを削除します。
- アカウント廃止時はキーリング/秘密 mount とサービス権限、API/OAuth 資格情報を除き、他フローが DKIM selector、DNS 承認、Webhook、バウンス先を使わないか確認後に変更します。
- 最後に孤立スケジューラー、公開 listener、未処理バウンス、滞留キュー、長期機密ログがないことを確認します。
管理下の 1 通に対する受入チェック
- 送信アカウント、ドメイン、ホスト、唯一のテスト宛先に明示的権限がある。
- 最新公式文書からホスト、ポート、TLS、認証、送信者、割当、バウンス手順を確認した。
- ローカル MTA、認証 submission、API を区別し、任意 MX 直送やオープンリレーがない。
- TLS ホスト名と証明書検証に成功し、暗号化後だけ資格情報を使う。
- password/token が argv、URL、repo、本文、ログ、画像、履歴にない。
- envelope-from、
From:、宛先、SPF/DKIM/DMARC の所有関係が整合する。 - ローカル/プロバイダー sink を通し、本番は管理下宛先へ 1 通だけ送る。
- 脱敏済み submission ID、遠隔結果、認証ヘッダーを残し、「受理」と受信箱/開封を区別する。
- 速度、再試行、バウンス、苦情、停止、抑止、プライバシー保存に担当者がいる。
- 停止、正確なロールバック、資格情報失効、オフボーディングを検証した。
参考資料
- RFC 6409:Message Submission for Mail
- RFC 8314:メール submission/access の TLS
- RFC 5321:SMTP と転送エンベロープ
- RFC 5322:Internet Message Format
- RFC 7628:OAuth の SASL mechanism
- msmtp 1.8.34 公式手引き
- Postfix 公式 `sendmail(1)` 互換インターフェース手引き
- OpenSSL `s_client` 公式手引き
- RFC 7208:SPF
- RFC 6376:DKIM
- RFC 9989:DMARC(RFC 7489 を置換)
歴史的原文アーカイブ
以下は source_export にある 2014 年の可視本文を完全保存した不活性アーカイブです。古い識別可能なメール/単一メッセージ情報を再公開しないため 6 か所だけ置換しました。内訳はメールボックスパスのユーザー名 1、一覧メール 1、ローカル識別子 1、メールアドレス 2、Message-ID 1 です。一般教程内の frank/renee、歴史的ドメイン、古い HTTP 出典リンク 2 本は未変更で、リンクは来歴にすぎず現在のサービス、ダウンロード、設定推奨ではありません。原文に行末空白はなく、他は変更していません。囲い内のコマンド、アドレス、手法を実行しないでください。
Table of Contents
Toggle
- [Linux下mail使用技巧](https://blog.lazying.art/en/html/computer_internet/unix_linux/command_shell_software/1143/linux%e4%b8%8b%e9%82%ae%e4%bb%b6mail%e6%9f%a5%e6%94%b6.html/#Linux%E4%B8%8Bmail%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7)
# Linux下mail使用技巧
登录LINUX系统后,经常会看到”you have mail”,却苦于不知道如何查看,相信菜鸟们都遇到过,偶在网上用“linux mail”找了很久,但大都是介绍[mail服务器](http://earnfs.sinaapp.com/html/1141.htm)的,黄天总算没负有心人,在洪恩在找到一篇介绍基础的文章,不敢独享。
系统提供了用户之间通信的邮件系统,当用户打开终端注册登录时发现系统给出如下信息:
you have mail.
这时用户可通过键入mail命令读取信件:
$ mail
mail程序将逐个显示用户的信件,并依照时间顺序,显示最新的信件。每显示一段信件,mail都询问用户是否要对该信件作些处理。若用户回答d,则表示 删除信件;若仅按回车键,表示对信件不作任何改动(信件仍旧保存,下次还可读这一信件);若回答p,则要求重复显示信件;s filename表示要把信件存入所命名的文件;若回答q,表示要从mail退出。
我们在本章的第一个例子中演示了如何写一封信,作为练习,你可送信件给自己,然后键入mail读取自己发的信件,看看会有什么效果。(发信给自己是一种设置备忘录的方法)。
$mail frank 给自己写信
subject: test
This is a mail test
CRL-d
EOT
$
$mail 查看信件
“/var/spool/mail/[historical user redacted]:”1 message 1 new
>N[historical email redacted]Thu Mar 25 11:00 13/403 “test”
&
Message 1:
From frank Thu Mar 25 11:00:25 1999/3/25
Received: ([historical local identity redacted])
by xteam.xteamlinux.com(8.8.4/8.8.4)
id LAA05170 for frank;Thu 25 Mar 1999 11:00:25 GMT
Date: Thu,25 Mar 1999 11:00:25 GMT
From:RHS Linux User <[historical email redacted]>
Message-Id:<[historical message identifier redacted]>
To:[historical email redacted]
Subject:test
Status:R
This is a mail test
&
mail命令还有很多其它用法,例如发送事先准备好的信件,或一次送信给若干人。还可以用其它方法送信件。
另附message的使用技巧:
当Linux系统处于多用户的情况下,有时在终端上会突然显示出下述信息:
Message from renee tty2…
并伴随出现一阵嘟嘟响声。这是用户renee想和你通话而产生的信号。若你用如下命令响应他:
$ write renee
这就建立起了你和renee的通信线路,renee在他的终端上键入的内容同时显示在你的终端上,反之你键入的内容也显示在renee的终端上。为区分终 端上哪些是你输入的,哪些是renee输入的,我们使用如下通话协议:(o)表示一段话说完,并让对方发话,(oo)代表通话结束并退出程序。
renee’s terminal: frank terminal:
[renee@xteam renee]$ write frank
$ Message from renee tty2…
$write renee
[renee@xteam renee]$Message from you tty1…
did you forget lunch? (o)
did you forgeet lunch? (o)
ten minutes (o)
ten minutes (o)
ok (oo)
ok (oo)
ctl-d
EOF
Ctl-d
EOF
[renee@xteam renee]$ $
除CTL-d键外,也可以使用DELETE退出write命令。
如果你不愿意别人干扰你的工作,可以使用mesg命令拒绝接受通话。当你向一个拒绝接收通话的用户发写命令、或者向没有注册的用户要求通话时,write命令会显示不能通话的原因。
转自:http://edu.codepub.com/2010/0413/21978.php
