logrotateでrsyslogへのSIGHUP送信をたくさん書いてはいけない話

前提

logrotateの設定で、rsyslogを再起動させたいときにSIGHUPを送る。以下はRHEL 9の例。

/var/log/cron
/var/log/maillog
/var/log/messages
/var/log/secure
/var/log/spooler
{
    missingok
    sharedscripts
    postrotate
        /usr/bin/systemctl -s HUP kill rsyslog.service >/dev/null 2>&1 || true
    endscript
}

複数SIGHUPの問題

上記の設定はごく普通で、これだけだと何の害もないが、ローテート間隔が違うファイルを扱うなどで複数のSIGHUP送信を書くと問題がある。

  • rsyslogはSIGHUPのハンドラを実行している最中にSIGHUPを受信すると異常終了する。

多少緩和されていて、rsyslog.serviceに Restart=on-failure が設定されているので、SIGHUP連打が発生してもsystemdが自動再起動を行う。

もちろんソケットは一旦切れるのでtcp/udpでsyslogを受信している場合はログ欠損が起きる。

さらに、自動再起動が5回以上になるとレート制限(start-limit-hit)によりsystemdの自動再起動も失敗する。 つまりlogrotateの設定でSIGHUP送信をたくさん行うとrsyslogが意図せずlogrotateのタイミングや(しばらく停止したあとの)起動直後のタイミングで失敗するケースが発生しうる。タイミングにより必ず発生するわけではないあたりがいやらしい。

対策

RHEL 10やFedora では既に対策されている。rsyslog.serviceではExecReload でSIGHUPを送信するように設定して、 logrotateからは systemctl reload rsyslog でSIGHUPを送信する。

# /usr/lib/systemd/system/rsyslog.service
[Unit]
Description=System Logging Service
;Requires=syslog.socket
Documentation=man:rsyslogd(8)
Documentation=https://www.rsyslog.com/doc/
Wants=network.target network-online.target
After=network.target network-online.target

[Service]
Type=notify
EnvironmentFile=-/etc/sysconfig/rsyslog
ExecStart=/usr/sbin/rsyslogd -n $SYSLOGD_OPTIONS
ExecReload=/usr/bin/kill -HUP $MAINPID     <<====
UMask=0066
StandardOutput=null
Restart=on-failure
(以下略)
# /etc/logrotate.d/rsyslog
/var/log/cron
/var/log/maillog
/var/log/messages
/var/log/secure
/var/log/spooler
{
    missingok
    sharedscripts
    postrotate
        /usr/bin/systemctl reload rsyslog.service >/dev/null 2>&1 || true    <<===
    endscript
}

対策のしくみ

どのバージョンからか確認していないがrsyslogはsystemd notify(オペレーションできる状態になったとsystemdに通知する仕組み)に対応していて、rsyslog.service は Type=notifyになっている。 この場合reloadは、ExecReloadのコマンドを実行したあとsystemd notifyを受信するまで完了しない。 rsyslogはSIGHUPの処理が終わるまで systemd notifyを送信しないので、systemctl restart なら連打されても割り込み処理中に再度割り込みが入ることがない。

メモ: RHEL関連サーチ設定

Selection Search - Chrome Web Store で自分が実際に使っているもののうち、社内でしか使えないものをのぞいたリスト

  • RHEL rpm: https://access.redhat.com/downloads/content/%s/x86_64/package-latest

  • Issue:

    • Jira ID: https://issues.stage.redhat.com/browse/%s
    • BZ ID: https://bugzilla.redhat.com/show_bug.cgi?id=%s
    • BZ Search: https://bugzilla.redhat.com/buglist.cgi?bug_status=__all__&content=%22%s%22&list_id=13536385&order=Importance&query_format=specific
    • BZ Google: https://www.google.com/search?q=%s+site%3Abugzilla.redhat.com
  • ナレッジ:

    • CP Google: https://www.google.com/search?q="%s"+site%3Aaccess.redhat.com
    • CP RHEL KB: https://access.redhat.com/kb/search?search=%s&products=Red+Hat+Enterprise+Linux&start=0&rows=100
    • CP KB: https://access.redhat.com/kb/search?search=%s&start=0&rows=100
    • CPSEARCH: https://access.redhat.com/search/#/?q=%s
  • ドキュメント:

    • RHEL7 Doc: https://www.google.com/search?q="%s"+site%3A%2F%2Fdocs.redhat.com%2Fen%2Fdocumentation%2Fred_hat_enterprise_linux%2F7%2Fhtml%2F
    • RHEL8 Doc: https://www.google.com/search?q="%s"+site%3A%2F%2Fdocs.redhat.com%2Fen%2Fdocumentation%2Fred_hat_enterprise_linux%2F8%2Fhtml%2F
    • RHEL9 Doc: https://www.google.com/search?q="%s"+site%3A%2F%2Fdocs.redhat.com%2Fen%2Fdocumentation%2Fred_hat_enterprise_linux%2F9%2Fhtml%2F
    • RHEL10 Doc: https://www.google.com/search?q="%s"+site%3A%2F%2Fdocs.redhat.com%2Fen%2Fdocumentation%2Fred_hat_enterprise_linux%2F10%2Fhtml%2F
    • RHDS11 Doc: https://www.google.com/search?q="%s"+site%3A%2F%2Fdocs.redhat.com%2Fen%2Fdocumentation%2Fred_hat_directory_server%2F11
    • RHDS12 Doc: https://www.google.com/search?q="%s"+site%3A%2F%2Fdocs.redhat.com%2Fen%2Fdocumentation%2Fred_hat_directory_server%2F12
    • RHDS13 Doc: https://www.google.com/search?q="%s"+site%3A%2F%2Fdocs.redhat.com%2Fen%2Fdocumentation%2Fred_hat_directory_server%2F13
  • CVE DB: https://access.redhat.com/security/cve/%s

  • Errata: https://access.redhat.com/errata/%s

自分用メモ 2025年4月時点でのChatGPTカスタマイズ

ChatGPT にどのような特徴を求めていますか?

賞賛よりも内容を重視しましょう。不必要​​な褒め言葉や深みのない褒め言葉は控えましょう。私の考えに批判的に向き合い、前提に疑問を投げかけ、偏見を指摘し、必要に応じて反論しましょう。正当な理由がある場合は、意見の相違をためらわず、合意には必ず根拠と証拠が必要です。
疑問を持ちながら論理的に考えます。回答の詳細に気を配り正確な回答をおこないます。
ユーザの意見より実際の真実であることを重視します。ユーザと食い違いがある場合は根拠を示します。単純に回答を貰うよりは議論をおこなって理解を深めたいです。事実についての表明の場合は関連資料・webサイトへのリンクをおこないます。
価値の高い回答をするため、利用者から情報を得ることで改善できそうな場合や見逃している点がありそうな場合、回答をする前に疑問点やアイデアを提示して反応を得ます。このときは発展的な話題はしません。
疑問点やアイデアは1つずつ提示して、選択肢には(A),(B),(C),(D) のようなラベルをつけて反応しやすくします。
反応を得て回答をしたあとには、関連する発展的な話題を提示します。発展的な話題にも(1),(2),(3),(4),(5)のようなラベルをつけて反応しやすくします。
数式の利用は問題ありません。知的な刺激を与えるような関連事項があれば併記して。
もしリンクを表記する時はURLを直接表示して "**タイトル**: https://host/path/to/page.html " の形式で記述します。

プロジェクト → ページ解説

URLにある記事を読み、その内容をもとに日本語の解説記事を作成してください。

- 解説はできるだけ詳細にし、元記事を読まなくても内容がわかるようにしてください。
- 元の記事の事実情報をできる限り忠実に反映してください。
- 解説の中で筆者の意見や主観的な見解が登場する場合は、それが「意見」であることを明示し、事実との区別を明確にしてください。
- 文体は中立的で淡々とした解説スタイルをとり、感情的・扇動的な表現は避けてください。
- 出力は日本語でお願いします。

プロジェクト→英語学習

あなたは英語学習支援AIです。以下の要件で、対話形式の学習サポートを行ってください。

【目的】
ユーザーが提示した英語学習トピックをもとに、学習内容を体系化し、理解度チェックと学習計画の提示を行います。

【手順】
1. ユーザーが提示したトピックに基づき、その内容をツリー状に分類し、関連するサブトピックや前提知識をリストアップしてください。
2. トピックおよび前提知識に関連した30問程度の確認問題を、対話形式で1問ずつ出題してください。
   - 問題形式は選択式・記述式を織り交ぜてください。
   - 問題ごとにユーザーの回答を確認し、理解度に応じて次の問題を調整してください。
   - 前提知識の不足が見られる場合は、それに関連する問題も挿入してください。
3. 全問題終了後、以下をまとめて提示してください:
   - 現時点での理解範囲の評価
   - 学習が必要なサブトピックと前提知識のリスト
4. 上記からユーザーが学びたいサブトピックを選択したら、そのトピックについて再びステップ1から繰り返してください。

【前提設定】
- ユーザーはCEFR B2レベルの日本語話者です。
- スキル重視:リスニング、語彙、スピーキング、ライティング、英語での思考力
- 出力はできる限り簡潔で明確にしてください。質問は1つずつ行い、学習者のストレスを軽減してください。

最初に、ユーザーに「どのトピックについて学びたいですか?」と尋ね、対話を始めてください。

自分用メモ 2025年3月時点でのChatGPTカスタマイズ

ChatGPT にどのような特徴を求めていますか?

ストレートな物言いをして、オブラートに包んだ回答はしません。疑問を持ちながら論理的に考えます。文脈を失わないようにしつつコンパクトな表現を使います。回答の詳細に気を配り正確な回答をおこないます。
ユーザの意見より実際の真実であることを重視します。ユーザと食い違いがある場合は根拠を示します。単純に回答を貰うよりは議論をおこなって理解を深めたいです。事実についての表明の場合は関連資料・webサイトへのリンクをおこないます。リンクを書く前に実際にアクセスして内容を確認します。
価値の高い回答をするため、利用者から情報を得ることで改善できそうな場合や見逃している点がありそうな場合、回答をする前に疑問点やアイデアを提示して反応を得ます。このときは発展的な話題はしません。
疑問点やアイデアは1つずつ提示して、選択肢には(A),(B),(C),(D) のようなラベルをつけて反応しやすくします。
反応を得て回答をしたあとには、関連する発展的な話題を提示します。発展的な話題にも(1),(2),(3),(4),(5)のようなラベルをつけて反応しやすくします。
数式の利用は問題ありません。知的な刺激を与えるような関連事項があれば併記して。

リンクを書く前にアクセス、はしてくれたことがないな……消してよさそう。

GPT-4oで 画像生成

GPT-4oの画像生成がよくなって日本語の文字とかかなりきっちり含められるようになった。

  • 独立した要素を20個くらいまでは安定して併記できるので複数登場人物生成もよいかんじでできる。
  • 生成される画像はノイズが多いので、https://pixfix.com/ あたりでノイズ除去したほうがいい。
  • 画像の入力を受けとりかなり詳細まで再現できる
  • スタイルについて指定に対する忠実度はひくめ。

    • スタイルの詳細を指定してもあまり追従しない。たとえば輪郭線がないと指定しても勝手に描いてくる(いらすとやの再現に苦労する)。微調整よりもGPT-4o内にあるスタイルに集約させる力が強い感じ。
    • 逆に名前がついているスタイルで生成するとすごくそれっぽいのが出てくる。同一スタイルでのスタイルの一貫性は高い。ジブリ風のミームが大量生産されている。
    • 例示した画像との特徴はよく保存する。スタイルは類似したものが適当に選ばれる感じ。

自分用メモ: 2025年2月時点のChatGPTカスタマイズ

ChatGPT をカスタマイズする → ChatGPT にどのような特徴を求めていますか?

ストレートな物言いをして、オブラートに包んだ回答はしません。疑問を持ちながら論理的に考えます。文脈を失わないようにしつつコンパクトな表現を使います。
ユーザの意見より実際の真実であることを重視します。ユーザと食い違いがある場合は根拠を示します。
価値の高い回答をするため、利用者から情報を得ることで改善できそうな場合、回答をする前に疑問点やアイデアを提示して反応を得ます。このときは発展的な質問はしません。
疑問点やアイデアは1つずつ提示して、選択肢には(A),(B),(C) のようなラベルをつけて反応しやすくします。
反応を得て回答をしたあとには3つ、関連する発展的な質問を提示します。発展的な質問にも(1),(2),(3)のようなラベルをつけて反応しやすくします。

プロジェクト→翻訳

Translate user inputs to Japanese, without modifying format or links.
Don't add any header or footer. Just put translated text.
Even if the content has header, TOC, banner or comment, don't ask about them and just translate them, too.
Don't stop translation in the middle. Put all translation at once.

プロジェクト→英単語使い分け表

与えられた英単語について、それぞれの意味の違いが際立つような適切なコンテキストを自動生成し、それに基づいた比較表を作成してください。  

### 指示  
1. 各単語の意味を分析し、意味の違いが明確になるような使用コンテキストを選定する。
   - コンテキストの数は単語の特性に応じて変動させること。  
   - 各単語の違いが際立つような文脈を優先する。  
2. 与えられた単語の類義語や関連語があれば、それも含めて比較する。
   - 複数の単語に共通する類義語がある場合、もともと指定されていない単語でも表に追加する。
   - 追加された単語についても、適切なコンテキストごとの例文を作成する。  
3. Markdown形式の表で出力する。  
4. 各単語ごとに例文を作成し、自然さを評価する。
   - 自然な例文には「○」をつける。  
   - やや不自然な例文や独特のニュアンスが含まれる場合には「△」をつけ、括弧内で不自然な理由やニュアンスの違いを説明する。
   - その単語がそのコンテキストでは自然に利用できない場合、例文の代わりに「N/A」と記載する。
5. 表のあとに各単語を使いわけるための備考を記載する。
6. 例文以外の説明は日本語で記述する。

---

### 出力フォーマット  

#### **単語リスト:**  
与えられた単語と、それに関連する類義語をリストアップする。  

#### **コンテキストごとの比較表:**  

| 使用コンテキスト            | Word 1               | Word 2               | Word 3               | Added Synonym 1      | Added Synonym 2      | ... |  
|-----------------------------|----------------------|----------------------|----------------------|----------------------|----------------------|-----|  
| **コンテキスト 1**           | Example sentence ○ / △ (理由) | Example sentence ○ / △ (理由) | Example sentence ○ / △ (理由) | Example sentence ○ / △ (理由) | Example sentence ○ / △ (理由) | ... |  
| **コンテキスト 2**            | Example sentence ○ / △ (理由) | Example sentence ○ / △ (理由) | Example sentence ○ / △ (理由) | N/A | Example sentence ○ / △ (理由) | ... |  
| **コンテキスト 3**            | Example sentence ○ / △ (理由) | N/A | Example sentence ○ / △ (理由) | Example sentence ○ / △ (理由) | N/A | ... |  
| ...                         | ...                  | ...                  | ...                  | ...                  | ...                  | ... |  
#### **備考** 

 * Word 1の特徴 
 * Word 2の特徴 
 *   ...  
 *   ...  

ssh コマンドのデバッグ出力から使われてる暗号化方式を読みとる

nmapつかおうねという話。

前ふり: ssh の暗号化方式

sshは沢山の暗号化方式を扱います。

  • 通信をはじめる前のDH鍵交換 (KexAlgorithms)
  • ホストを確認するためのホスト鍵や個人を確認するための鍵 (HostKeyAlgorithms, PubkeyAcceptedAlgorithms)
  • 通信の暗号化 (Ciphers)
  • 暗号化されたメッセージの認証 (MACs)
  • などなど

暗号化方式の中には既に危険なもの、十分安全ではないものがあるので、設定によりそれらを排除します。 sshdの設定を外部から確認するには、 nmapを利用して以下のようにします。

$ nmap --script ssh2-enum-algos -sV -p 22 127.0.0.1

`Starting Nmap 7.92 ( https://nmap.org ) at 2025-02-07 09:12 JST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00021s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.8 (protocol 2.0)
| ssh2-enum-algos: 
|   kex_algorithms: (11)
|       curve25519-sha256
|       curve25519-sha256@libssh.org
|       ecdh-sha2-nistp256
|       ecdh-sha2-nistp384
|       ecdh-sha2-nistp521
|       diffie-hellman-group-exchange-sha256
|       diffie-hellman-group14-sha256
|       diffie-hellman-group16-sha512
|       diffie-hellman-group18-sha512
|       ext-info-s
|       kex-strict-s-v00@openssh.com
|   server_host_key_algorithms: (4)
|       rsa-sha2-512
|       rsa-sha2-256
|       ecdsa-sha2-nistp256
|       ssh-ed25519
|   encryption_algorithms: (5)
|       aes256-gcm@openssh.com
|       chacha20-poly1305@openssh.com
|       aes256-ctr
|       aes128-gcm@openssh.com
|       aes128-ctr
|   mac_algorithms: (8)
|       hmac-sha2-256-etm@openssh.com
|       hmac-sha1-etm@openssh.com
|       umac-128-etm@openssh.com
|       hmac-sha2-512-etm@openssh.com
|       hmac-sha2-256
|       hmac-sha1
|       umac-128@openssh.com
|       hmac-sha2-512
|   compression_algorithms: (2)
|       none
|_      zlib@openssh.com

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 0.30 seconds

縛りプレイ: nmap禁止

ssh の通信では、サーバとクライアントがそれぞれ自分の使える暗号化方式の一覧を相手に宣言して、 そのANDをとって採用する暗号化方式をきめます。

デバッグ出力をみると、利用する暗号化方式のやりとりを観察できます。 メッセージがどうなるかはバージョン依存です。手元のfedoraだとこんなかんじ。。。

$ ssh -vv localhost
OpenSSH_9.8p1, OpenSSL 3.2.2 4 Jun 2024
debug1: Reading configuration data /home/moriwaka/.ssh/config
(設定読み込み略)

(1) TCP接続して

debug2: resolving "localhost" port 22
debug1: Connecting to localhost [::1] port 22.
debug1: Connection established.
(private key略)

(2) 互いのバージョン確認、認証しよう

debug1: Local version string SSH-2.0-OpenSSH_9.8
debug1: Remote protocol version 2.0, remote software version OpenSSH_9.8
debug1: compat_banner: match: OpenSSH_9.8 pat OpenSSH* compat 0x04000000
debug2: fd 3 setting O_NONBLOCK
debug1: Authenticating to localhost:22 as 'moriwaka'
(private key略)

(3) 鍵交換しよう! クライアント側の 扱える暗号化はこう

debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug2: local client KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,ext-info-c,kex-strict-c-v00@openssh.com
debug2: host key algorithms: ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ssh-ed25519@openssh.com,sk-ecdsa-sha2-nistp256@openssh.com,rsa-sha2-512,rsa-sha2-256
debug2: ciphers ctos: aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes128-gcm@openssh.com,aes128-ctr
debug2: ciphers stoc: aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes128-gcm@openssh.com,aes128-ctr
debug2: MACs ctos: hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha1,umac-128@openssh.com,hmac-sha2-512
debug2: MACs stoc: hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha1,umac-128@openssh.com,hmac-sha2-512
debug2: compression ctos: none,zlib@openssh.com,zlib
debug2: compression stoc: none,zlib@openssh.com,zlib
debug2: languages ctos: 
debug2: languages stoc: 
debug2: first_kex_follows 0 
debug2: reserved 0 

(4) サーバから鍵交換の返事きた。サーバ側の扱える暗号化方式はこう。

冒頭のnmapのスクリプトはサーバと通信して、ここに相当する処理をして結果を出力しています。

debug2: peer server KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,ext-info-s,kex-strict-s-v00@openssh.com
debug2: host key algorithms: rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp256,ssh-ed25519
debug2: ciphers ctos: aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes128-gcm@openssh.com,aes128-ctr
debug2: ciphers stoc: aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes128-gcm@openssh.com,aes128-ctr
debug2: MACs ctos: hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha1,umac-128@openssh.com,hmac-sha2-512
debug2: MACs stoc: hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha1,umac-128@openssh.com,hmac-sha2-512
debug2: compression ctos: none,zlib@openssh.com
debug2: compression stoc: none,zlib@openssh.com
debug2: languages ctos: 
debug2: languages stoc: 
debug2: first_kex_follows 0 
debug2: reserved 0 

(5)プロフィール交換したんで実際kexやるよ

debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: ssh-ed25519
debug1: kex: server->client cipher: aes256-gcm@openssh.com MAC: <implicit> compression: none
debug1: kex: client->server cipher: aes256-gcm@openssh.com MAC: <implicit> compression: none
debug1: kex: curve25519-sha256 need=32 dh_need=32
debug1: kex: curve25519-sha256 need=32 dh_need=32

(6) 返事きてkex成功したね

debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: SSH2_MSG_KEX_ECDH_REPLY received
(以下略)

サーバ設定の確認

(4)の箇所から確認します。なんとなく読めますが……。

debug2: KEX algorithms: curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,ext-info-s,kex-strict-s-v00@openssh.com

ext-info-s はサーバ側であることを示しています。kex-strict-s-v00@openssh.com は実際の暗号化方式ではなくダミーで、鍵交換方式のバリエーションを示します。

debug2: host key algorithms: rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp256,ssh-ed25519

→ ホスト鍵で利用できるアルゴリズム一覧です。対応するホスト鍵がない場合もあります。

debug2: ciphers ctos: aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes128-gcm@openssh.com,aes128-ctr
debug2: ciphers stoc: aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes128-gcm@openssh.com,aes128-ctr
debug2: MACs ctos: hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha1,umac-128@openssh.com,hmac-sha2-512
debug2: MACs stoc: hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha1,umac-128@openssh.com,hmac-sha2-512
debug2: compression ctos: none,zlib@openssh.com
debug2: compression stoc: none,zlib@openssh.com
debug2: languages ctos: 
debug2: languages stoc: 

→ 'ctos', 'stoc'はclientから送信するとき、serverから送信するときの通信方向に対応します。 暗号化方式だけでなく圧縮方式などのやりとりもしています。

結論: nmapをつかおう

サーバ側の設定を追加ツールなしでも読みとれることがわかりました。ただし「できること」と「やるべきこと」は違います。運用においては nmap や他のツールを使いましょう。

  • (3)と(4)を見比べるとわかりますが、字面で全く同じであっても登場する場所によりクライアント側の話か、サーバ側の話かが違います。人間による読解は間違いがあるので第一の選択肢ではありません。
  • ログメッセージは規格でもなんでもないので、バージョンが違えば表示有無、ログレベル、テキスト表記等が変わるようなことは当然発生します。デバッグログをテキスト処理することはそれらの変化に追従するメンテナンスコストが必要な、まずい選択肢です。
  • sshの規格を知らないと意味がわからないext-info-s や openssh の独自拡張があり、「暗号化方式のリスト」に単純に一致するわけではありません。
  • 利用するツールを nmap に限定する必要はないですが、まともなツールを使えば上記の問題は全て避けられます

systemdでIPAddressAllow, IPAddressDenyを設定したときのメモ

systemdにはIPAddressAllow, IPAddressDenyというディレクティブがあり、利用するとeBPFを利用した簡易なファイアウォールが設定されます。

  • iptables/nftablesとは無関係に動き、他とまとめて操作・確認するような方法はありません。
  • systemctl statusなどで関係する設定を一覧するような機能はなく、各サービスのsystemdのunit fileを読むのが一番はやくて便利です。
  • 設定ではなく動いているものがみたいときはbpftoolで対応するmapを見ることはできるが、実装内部の話になりお世辞にも読みやすいとは言えない。

個人的な感想としては、特定IPアドレスだけとしか通信しないことが確定しているサービスのパッケージ作成時くらいしか出番はなさそう。

systemdでの設定

[Service]
IPAddressDeny=any
IPAddressAllow=192.168.1.0/24
IPAddressAllow=10.0.0.0/8

chronyd.service に設定して再起動したログ例

 1月 09 16:27:42 turtle audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=chronyd comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
 1月 09 16:27:42 turtle systemd[1]: chronyd.service: Consumed 4min 15.225s CPU time.
 1月 09 16:27:42 turtle audit: BPF prog-id=485 op=LOAD   <<=== BPF の操作はaudit logに記録
 1月 09 16:27:42 turtle audit: BPF prog-id=486 op=LOAD
 1月 09 16:27:42 turtle audit: BPF prog-id=487 op=LOAD
 1月 09 16:27:42 turtle audit: BPF prog-id=391 op=UNLOAD  <<== 不要になったBPFはunloadする
 1月 09 16:27:42 turtle audit: BPF prog-id=390 op=UNLOAD
 1月 09 16:27:42 turtle audit: BPF prog-id=389 op=UNLOAD
 1月 09 16:27:42 turtle systemd[1]: Starting chronyd.service - NTP client/server...
 1月 09 16:27:42 turtle chronyd[3890870]: chronyd version 4.6.1 starting (+CMDMON +NTP +REFCLOCK +RTC +PRIVDROP +SCFILTER +SIGND +ASYNCDNS +NTS +SECHASH +IPV6 +DEBUG)
 1月 09 16:27:42 turtle chronyd[3890870]: Using right/UTC timezone to obtain leap second data
 1月 09 16:27:42 turtle chronyd[3890870]: Frequency -20.746 +/- 0.733 ppm read from /var/lib/chrony/drift
 1月 09 16:27:42 turtle chronyd[3890870]: Loaded seccomp filter (level 2)
 1月 09 16:27:42 turtle chronyd[3890870]: Added source 132.163.97.4     <<=== 設定から解決しているのでsourceの設定はおこなうが……
 1月 09 16:27:42 turtle systemd[1]: Started chronyd.service - NTP client/server.
 1月 09 16:27:42 turtle audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=chronyd comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
 ...
 1月 09 16:27:59 turtle chronyd[3890870]: Can't synchronise: no selectable sources   <<== 通信できなくて同期しない

unitとbpfプログラムの対応関係

bpftool cgroup tree でcgroup←→bpf prog がわかるので、どのunitとの対応するかがわかる

# bpftool cgroup tree
CgroupPath
ID       AttachType      AttachFlags     Name      
...         
/sys/fs/cgroup/system.slice/chronyd.service
    487      cgroup_inet_ingress multi           sd_fw_ingress                  
    486      cgroup_inet_egress multi           sd_fw_egress                   
    485      cgroup_device   multi           sd_devices                
...

bpftool での詳細出力

bpftool prog, bpftool map で詳細が出力できるがこれで調査をおこなうにはbpfまわりの事前知識が必要:

# bpftool prog
...
485: cgroup_device  name sd_devices  tag ccbbf91f3c6979c7  gpl
    loaded_at 2025-01-09T16:27:42+0900  uid 0
    xlated 560B  jited 364B  memlock 4096B
    pids systemd(1)
486: cgroup_skb  name sd_fw_egress  tag df35b3b526089f21  gpl
    loaded_at 2025-01-09T16:27:42+0900  uid 0
    xlated 184B  jited 143B  memlock 4096B  map_ids 45
    pids systemd(1)
487: cgroup_skb  name sd_fw_ingress  tag 208d1bf35e7113d2  gpl
    loaded_at 2025-01-09T16:27:42+0900  uid 0
    xlated 184B  jited 143B  memlock 4096B  map_ids 45
    pids systemd(1)

# bpftool prog dump xlated id 487  
   0: (bf) r6 = r1
   1: (69) r7 = *(u16 *)(r6 +180)
   2: (b4) w8 = 0
   3: (55) if r7 != 0x8 goto pc+14
   4: (bf) r1 = r6
   5: (b4) w2 = 12
   6: (bf) r3 = r10
   7: (07) r3 += -4
   8: (b4) w4 = 4
   9: (85) call bpf_skb_load_bytes#12499728
  10: (18) r1 = map[id:45]     
  12: (bf) r2 = r10
  13: (07) r2 += -8
  14: (62) *(u32 *)(r2 +0) = 32
  15: (85) call trie_lookup_elem#338112
  16: (15) if r0 == 0x0 goto pc+1
  17: (44) w8 |= 1
  18: (44) w8 |= 2
  19: (b7) r0 = 1
  20: (55) if r8 != 0x2 goto pc+1
  21: (b7) r0 = 0
  22: (95) exit

# bpftool map dump id 45   
key: 08 00 00 00 0a 00 00 00  value: 01 00 00 00 00 00 00 00
key: 18 00 00 00 c0 a8 01 00  value: 01 00 00 00 00 00 00 00
Found 2 elements

プログラムのソース

プログラムの内容は systemd/src/core/bpf-firewall.c で定義 以下ブロックをipv4/v6, allow/denyの組み合わせで最大4回繰り返す

                /* Compare IPv4 with one word instruction (32-bit) */
                struct bpf_insn insn[] = {
                        /* If skb->protocol != ETH_P_IP, skip this whole block. The offset will be set later. */
                        BPF_JMP_IMM(BPF_JNE, BPF_REG_7, htobe16(protocol), 0),

                        /*
                         * Call into BPF_FUNC_skb_load_bytes to load the dst/src IP address
                         *
                         * R1: Pointer to the skb
                         * R2: Data offset
                         * R3: Destination buffer on the stack (r10 - 4)
                         * R4: Number of bytes to read (4)
                         */

                        BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
                        BPF_MOV32_IMM(BPF_REG_2, addr_offset),

                        BPF_MOV64_REG(BPF_REG_3, BPF_REG_10),
                        BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -addr_size),

                        BPF_MOV32_IMM(BPF_REG_4, addr_size),
                        BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_skb_load_bytes),

                        /*
                         * Call into BPF_FUNC_map_lookup_elem to see if the address matches any entry in the
                         * LPM trie map. For this to work, the prefixlen field of 'struct bpf_lpm_trie_key'
                         * has to be set to the maximum possible value.
                         *
                         * On success, the looked up value is stored in R0. For this application, the actual
                         * value doesn't matter, however; we just set the bit in @verdict in R8 if we found any
                         * matching value.
                         */

                        BPF_LD_MAP_FD(BPF_REG_1, map_fd),
                        BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
                        BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -addr_size - sizeof(uint32_t)),
                        BPF_ST_MEM(BPF_W, BPF_REG_2, 0, addr_size * 8),

                        BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
                        BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
                        BPF_ALU32_IMM(BPF_OR, BPF_REG_8, verdict),
                };

                /* Jump label fixup */
                insn[0].off = ELEMENTSOF(insn) - 1;