加入者ごとに端末一台を約束するトンネルサービスが、その約束を MaxSessions から得ることはほとんどなく、最初のテストでたいていそれが証明されます。加入者は二つ目の ssh プロセスを開き、一つ目のセッションは動き続け、両方のトンネルがトラフィックを運びます。設定オプションはそれぞれ別のものを数えており、どれも端末を数えていません。まず約束に名前を付けることが、両者の選択を明白にします。

各オプションが実際に数えるもの

MaxSessions は、認証済みの一つのネットワーク接続が運ぶオープンセッションの数を制限します。ここでのセッションとは、シェル、exec 要求、SFTP のようなサブシステム、あるいは一つの SSH トランスポート内で多重化された同様のチャネルです。二回目の ssh 呼び出しは別のトランスポートを開き、改めて認証して独自の枠を取得します。一つ目の接続のセッション数は、それには関係ありません。

MaxStartups は、まだ認証されていない同時接続を、開始しきい値・率・上限しきい値として制限します。半開きのハンドシェイクが殺到しても、認証前のデーモンの資源を使い果たせないようにするためです。クライアントが認証すると、その接続は MaxStartups に対して数えられなくなり、誰かのセッションになります。ユーザーごとの割り当てだったことは一度もなく、上げても加入者ができることは増えません。

端末登録のルールは sshd のオプションではまったくありません。それは資格情報を発行し、どの端末がそれを保持しているかを追跡するシステムに属し、カウンターではなく登録・台帳・失効が必要だということを意味します。その要件を sshd の設定に混ぜ込むことが、こうした設計の多くが行き詰まる場所です。

MaxSessions が割り当てでなくなる場所

Match User ブロックは、そのユーザーにどの設定値が適用されるかを変えるので、サービスアカウントに厳しめの MaxSessions を設定する自然な場所です。できないのは接続をまたいだ集計で、オプションはトランスポートごとに評価されるためです。二つのトランスポートはそれぞれ独自の上限を受け取り、ユーザーはブロックが許す量の二倍を得ることになります。

転送は別の驚きです。TCP 転送にはセッションチャネルが不要なので、MaxSessions 0 はシェルとサブシステムを禁止しながら、-L と -R のトンネルは許可できます。転送だけを行い、セッション数をトンネルの割り当てとみなすサービスは、何も測っていません。この挙動はOpenSSH サーバー設定リファレンスに記載されており、オプション名を中心に設計する前に読む価値があります。

読み込まれていると思うファイルではなく、実際に有効な設定を出力してください。インクルードや drop-in ディレクトリのせいで、有効な値が編集した値と違うことがよくあります。

MaxStartups が守るのは加入者ではなくデーモン

MaxStartups の三部構成は、デーモンが新しい接続の一定割合を落とし始める前に、未認証の接続がいくつ存在できるかを表します。既定の形があるのは、未認証の SSH 接続は開くのは安いが保持するのは高くつくからです。これはサーバー側の資源保護の制御であり、全員に同時に適用されます。

ログインの嵐のときに上げるのは、デーモンの保護を入場と引き換えることで、正しい取引のこともあれば、常に意図的な取引です。二つ目のトンネルを求める加入者には、一つ目のトンネルがすでに認証済みなので何の効果もありません。両者を混同すると、間違った症状を直しながら同時にサーバーを弱める変更になります。

ログインがまとめて拒否されるときは、どちらのオプションに触る前に、デーモンのログで認証前のメッセージを確認してください。そのメッセージはどのガードが働いたかを示しており、ガード同士は交換できません。

ユーザーごとのスロットには共有状態が必要

真に一つのアクティブなトランスポートというポリシーには、sshd の外のどこかに保持する予約が必要です。受け入れの段階で、アカウントのスロットをアトミックに確保し、接続が閉じたら解放し、クライアントが閉じずに消えたら失効させ、新しい試行を拒否するか古いものを置き換えるかを決める必要があります。その最後の判断は、アクティブなトンネルを置き換えると誰かが切断されるため、技術的な細部ではなく製品の判断です。

予約は、アカウントを受け入れるすべてのサーバーで共有される必要もあります。独立したローカルカウンターを持つ二台のホストは二つの別々の上限を適用し、もう一方のゲートウェイ経由で再接続した加入者は二つ目のスロットを無料で得ます。状態を保存するものには、到達できないストアがすべての加入者を締め出してはいけないため、独自の障害時の挙動が必要です。

一端末の約束なら、登録された端末ごとに別の資格情報を発行し、その層で登録と失効を制御します。コピーされた秘密鍵はどこからでも認証できるので、ユーザーごとに一つの鍵を発行しても、何台のマシンが持っているかの証拠にはなりません。

二つの実際の接続でポリシーを確認する

ポリシーを書く前に、受け入れテストをコマンドとして書いてください。五つのケースは、一つ目がアクティブな間に二つ目のトランスポートを開くこと、一つのトランスポート内の複数チャネル、ポート転送だけを行うクライアント、リンク断の後の再接続、同じアカウントが別のゲートウェイに現れることです。

まず有効な上限を読み、次にポリシーが拒否すべき接続を開きます。このテストの両方の半分が重要で、どちらも他方の代わりにはなりません。

sshd -T | grep -iE "maxsessions|maxstartups|allowtcpforwarding|permitopen"
ss -tn state established '( sport = :22 )' | head
ssh -N -L 8080:127.0.0.1:80 [email protected]

二つのターミナルでトンネルコマンドを二回実行し、ポート 22 で確立された接続を数えます。ポリシーが一つのアクティブなトランスポートを主張しているのに両方がトラフィックを運ぶなら、そのポリシーは強制ではなく文書です。その隙間は、MaxSessions を下げて二つ目のトランスポートが気づくことを期待するのではなく、受け入れ層で直してください。

関連記事