# norizuki.com サーバー メモ

norizuki.com は Mac mini 2012（自宅設置、Macmini6,1/6,2）。**yokosawa.cc も同一サーバー上で動作予定**のため、yokosawa.cc 作業時もこの CLAUDE.md を参照すること。

> **2026-05-02 サーバークラッシュにより全構成リセット。Mojave→Catalinaへ再構築。** 以前のEl Capitan時代の構成（macOS Server / 独自Apache 2.4.51 / PHP 8.0.6 / MariaDB 10.4.6 等）は全て消失。本ドキュメントは2026-05-02 再構築後の状態を記録。

## 基本情報
- OS: macOS 10.15.7 Catalina（Mac mini 2012が公式対応する最終版）
- IP: `159.28.108.2`（NURO回線、グローバル直付け、NAT無し、netmask /29）
- PTR: `ip9f1c6c02.ap.nuro.jp.`（NXDOMAIN問題は依然残る）
- ユーザー: `niac`（管理者）
- パスワード: `niacroute0230`
- 認証鍵: `~/.ssh/norizuki_rsa`（**現在未配置、要再設定**）

## SSH接続

norizuki.com(159.28.108.2)はファイアウォール(pf)でSSH接続元をホワイトリスト制限しているため、**Mac Mini(159.28.108.1:22022)経由のProxyJumpでのみ接続可能**。`~/.ssh/config` に `norizuki-jump` と `norizuki.com` のProxyJump設定済み。

サーバー再構築でホスト鍵が変わったため、`~/.ssh/known_hosts` の旧エントリ削除が必要な場合あり。

**鍵未配置のため当面パスワード認証のみ:**
```bash
sshpass -p 'niacroute0230' ssh \
  -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa \
  -o PreferredAuthentications=keyboard-interactive,password \
  -o PubkeyAuthentication=no -o IdentitiesOnly=yes \
  niac@norizuki.com
```

**ファイルデプロイ（SCP）:**
```bash
sshpass -p 'niacroute0230' scp \
  -o HostKeyAlgorithms=+ssh-rsa -o PubkeyAcceptedKeyTypes=+ssh-rsa \
  -o PreferredAuthentications=keyboard-interactive,password \
  -o PubkeyAuthentication=no -o IdentitiesOnly=yes \
  <ローカルファイル> niac@norizuki.com:<リモートパス>
```

**重要: `-P 22` や `-o ProxyJump=none` を付けてはいけない。** SSH configのProxyJump設定を使って Mac Mini 経由で接続すること。直接 `159.28.108.2:22` や `159.28.108.1:22022` に SCP してもファイル転送はできない。

`IdentitiesOnly=yes` を付けないと "Too many authentication failures" になる（多くの鍵がエージェントに登録されているため）。

## sudo
```bash
echo niacroute0230 | sudo -S sh -c "..."
```

`brew install` 等の特殊コマンドはsudo timestampを認識しないため、必要時のみ `/etc/sudoers.d/niac-tmp` で `niac ALL=(ALL) NOPASSWD: ALL` を一時設定→作業後削除。

## アーキテクチャ（2026-05-02 再構築後）

### メールサーバー（稼働中）

| ポート | サービス | 状態 |
|---|---|---|
| 25 | Postfix SMTP（サーバー間受信） | LISTEN |
| 587 | Postfix Submission（クライアント送信、STARTTLS+SASL AUTH） | LISTEN |
| 143 | Dovecot IMAP（STARTTLS） | LISTEN |
| 993 | Dovecot IMAPS | LISTEN |

- **Postfix 3.2.2**（macOS Catalina標準版）`/usr/sbin/postfix` + `/etc/postfix/`
- **Dovecot 2.3.21.1**（手動ソースビルド）`/usr/local/dovecot23/sbin/dovecot` + `/usr/local/dovecot23/etc/dovecot/`
  - 2.4.3 は anvil panic バグで TLS+認証後すぐ切断される問題があり、2.3 系へダウングレード（2026-05-03）
  - 2.4.3 のソースとビルドツリーは `/tmp/dovecot-2.4.3/` に残置（参考用）
- LaunchDaemon: 
  - Postfix: `com.apple.postfix.master`（標準）
  - Dovecot: `/Library/LaunchDaemons/org.dovecot.dovecot.plist`
  - LE自動更新: `/Library/LaunchDaemons/com.norizuki.le-renew.plist`（毎日03:30）
- 認証: PAM（システムユーザー）、Dovecot SASL を Postfix submission に提供
- 証明書: Let's Encrypt（acme.sh）→ `/etc/ssl/norizuki/norizuki.crt`+`.key`

### メールアカウント

| エイリアス | システムユーザー | パスワード | 配信先 | シェル |
|---|---|---|---|---|
| akihiro@norizuki.com | menou (uid=502) | nonaw6809 | ローカル Maildir | /bin/bash（SSH可） |
| yasuhiro@norizuki.com | aneeson (uid=503) | nyaty471 | **aneeson@mac.com に転送のみ**（ローカル保存なし） | /usr/bin/false |
| yasumitsu@norizuki.com | boss (uid=504) | Yn131500 | ローカル Maildir | /bin/bash（local delivery のためログインシェル必須） |
| chikaco@norizuki.com | yuria (uid=505) | Yuria1002 | ローカル Maildir | /bin/bash（local delivery のためログインシェル必須） |

エイリアス定義: `/etc/aliases` の末尾。`postmaster:menou`、`root:menou` も設定。

### DKIM
- セレクタ: `default`
- 鍵: `/etc/postfix/dkim/default.private`（644）, `/etc/postfix/dkim/default.public`
- DNS TXT: `default._domainkey.norizuki.com`（既に登録済み、ローカル鍵と一致確認済み）
- 署名: `content_filter = dkimsign:dummy` → `master.cf` の `dkimsign` pipe service → `/usr/local/bin/dkim-filter`（bash wrapper）→ `/usr/local/bin/dkimsign`（dkimpy）が処理 → `sendmail -G` で再投入
- Python: `dkimpy 1.1.8`（`/usr/bin/pip3 install dkimpy` でシステムpython3に導入）
- **動作: SMTP 587 経由の送信メールのみ署名される**（pickup→sendmailのローカル投稿は `pickup -o content_filter=` でフィルタ意図的にバイパスされループ防止のため）

### Homebrew（一部のみ）
- `/usr/local/Homebrew/`（5.1.8）、Catalinaは Tier 3（公式サポート外）
- 導入済み: openssl@3, curl, cmake, pkgconf, openldap, lua@5.4, ca-certificates 等
- `/usr/local/opt/curl/bin/curl` を `HOMEBREW_CURL_PATH` で使う（標準curlはTLS問題でgithub等にアクセス不可）
- `HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_ANALYTICS=1` を必ず設定

### Dovecot ソースビルドの注意点
- brew formula は SHA256 不一致／macOS非対応で導入不可（Catalina + Tier3）
- **現用は 2.3.21.1**（`/usr/local/dovecot23/`）
  - ソース: `/tmp/dovecot-2.3.21.1.tar.gz`（upstream `https://dovecot.org/releases/2.3/...`）
  - **要パッチ**: `src/lib-storage/index/dbox-common/dbox-storage.c` の `st_atim`/`st_ctim` を `st_atimespec`/`st_ctimespec` に置換（macOS非互換）
  - configure: `--prefix=/usr/local/dovecot23 --sysconfdir=/usr/local/dovecot23/etc --localstatedir=/usr/local/dovecot23/var --with-pam --with-ssl=openssl --without-lua --without-ldap`
  - PKG_CONFIG_PATH=/usr/local/opt/openssl@3/lib/pkgconfig 必須
- 2.4.3（残置、未使用）: `/tmp/dovecot-2.4.3/`、`/usr/local/sbin/dovecot`
  - `--with-lua=no` を明示しないと `libdovecot-lua.la` リンクエラーで失敗
  - `imap-hibernate` バイナリが `make install` から漏れる（個別ビルド要）
  - **本番未投入の理由**: TLS+認証直後に anvil panic で接続切断（既知バグ）

### Dovecot dependencies (manually created)
- システムユーザー `_dovecot` (uid=214) と `_dovenull` (uid=227) は Catalina に同梱済み
- グループ `dovecot` (gid=6) と `dovenull` (gid=227) は新規作成（dscl）
- PAM service: `/etc/pam.d/dovecot`（`pam_opendirectory.so`）

### Gmail SMTP リレー（外部送信用、2026-05-02 設定）

NURO Biz の OP25B（アウトバウンド port 25 ブロック）回避のため、外部宛は Gmail SMTP リレー経由。

設定:
```
# /etc/postfix/main.cf
relayhost = [smtp.gmail.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_sasl_tls_security_options = noanonymous
smtp_sasl_mechanism_filter = login   # ← Catalina の libsasl2 PLAIN は動作不良、LOGIN のみ指定
broken_sasl_auth_clients = yes
smtp_tls_security_level = encrypt
smtp_tls_CAfile = /etc/ssl/cert.pem
smtp_use_tls = yes
```

`/etc/postfix/sasl_passwd`:
```
[smtp.gmail.com]:587 a.norizuki@gmail.com:<アプリパスワード>
```
- 600 root:wheel
- `postmap` で `.db` 生成必要
- アプリパスワードは https://myaccount.google.com/apppasswords で生成（要2段階認証有効化）

**重要: `smtp_sasl_mechanism_filter = login` を入れないと Catalina の SASL 実装で "generic failure" となり認証失敗する**。これを LOGIN のみに絞ることで Gmail への AUTH 成功。

送信制限: Gmail SMTP は無料枠 500通/日。

### Web サーバー (Apache)

- Apache 2.4.41（Catalina 標準 `/usr/sbin/httpd`、ラッパー `/usr/sbin/httpd-wrapper`）
- 起動: LaunchDaemon `org.apache.httpd`（user=niac, group=staff）
- vhost 設定: `/etc/apache2/other/*.conf`（norizuki.com, aneeson.com, yokosawa.cc, atchat.org）
- DocumentRoot: `/Users/niac/web/<ドメイン>/`（**内蔵ストレージ**）
- PHP 7.3.11, LibreSSL 2.8.3 同梱

ホスティング中ドメイン:
| ドメイン | DocumentRoot | 編集権限 ACL | 備考 |
|---|---|---|---|
| norizuki.com / www | `/Users/niac/web/norizuki.com/` | `group:admin` フル | （ほぼ空、77B index.html） |
| aneeson.com / www | `/Users/niac/web/aneeson.com/` | `user:aneeson` フル | `Aneeson/Blog/Blog.html` ヘ refresh、~558MB。`/Users/aneeson/Sites/aneeson.com` にシンボリックリンク |
| yokosawa.cc / www | `/Users/niac/web/yokosawa.cc/` | `group:admin` フル + `_www` 読 | PHP 含む、~14MB |
| atchat.org / www / atchat.jp / www | `/Users/niac/web/atchat.org/` | `user:menou` フル | ~5.5MB |

ACL は全て `file_inherit, directory_inherit` 付きで新規ファイル/サブディレクトリに自動継承。Apache (`User niac`) は所有者として読み取り、明示 ACL されていないドメインも other-read 権限で配信可能。

### Web ディレクトリ自動バックアップ（2026-05-07 設定）

LaCieRAID2TB へ毎晩 03:00 に rsync。**`/Users/niac/web/` のリードのみ**。LaCie 側は読み出し用バックアップで Apache は内蔵を見ている（macOS Catalina の TCC により daemon プロセスは `/Volumes/*` 配下のファイルが `EPERM` 拒否されるため、LaCie 直接配信は不可）。

- スクリプト: `/usr/local/bin/web-backup.sh`
- LaunchDaemon: `/Library/LaunchDaemons/com.norizuki.web-backup.plist`（毎日03:00）
- 内容: `rsync -a --delete /Users/niac/web/ /Volumes/LACIERAID2TB/web/`
- ログ: `/var/log/web-backup.log`
- LaCie 未マウントなら skip（破壊的同期を避けるため）

**復旧手順**: 内蔵 SSD が壊れた場合は LaCie から `rsync -a /Volumes/LACIERAID2TB/web/ /Users/niac/web/` で戻す。

### データベース (MariaDB)

- **MariaDB 10.4.6**（Homebrew `/usr/local/opt/mariadb/`）
- 管理: **ユーザー LaunchAgent** `~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist`（Label `homebrew.mxcl.mariadb`、`KeepAlive=true` `RunAtLoad=true`、`mysqld_safe --datadir=/usr/local/var/mysql` を起動）
- **datadir: `/usr/local/var/mysql`（内蔵ストレージ・必須）**。plist の `--datadir` 引数が常に優先。**LaCie 等 `/Volumes/*` 配下は不可**（Web バックアップ同様、Catalina の TCC により daemon プロセスが `/Volumes/*` で EPERM 拒否される。過去 LaCie へ移設を試み、この制約で断念した経緯あり）
- socket: `/tmp/mysql.sock`、port: 3306、err ログ: `/usr/local/var/mysql/norizuki.com.err`、pid: `/usr/local/var/mysql/norizuki.com.pid`
- 設定ファイル: **`/usr/local/etc/my.cnf`**（mariadb の探索順は `/usr/local/etc/my.cnf` → `~/.my.cnf`）。現内容:
  ```
  [mysqld]
  socket=/tmp/mysql.sock
  bind-address=127.0.0.1
  [client]
  socket=/tmp/mysql.sock
  ```
- **`bind-address=127.0.0.1`（2026-05-18 設定）**: 外部インターフェースで待受けない。グローバル IP 直付けのため、これと下記 pf の 3306 遮断で外部からの接続枯渇/ブルートフォースを構造的に防止。
- ローカルアプリ（`as/` 等、同一サーバー上の PHP）は socket `/tmp/mysql.sock` 接続のため bind-address / pf 遮断の影響なし
- CLI は PATH 外。フルパスで `/usr/local/opt/mariadb/bin/mysql --socket=/tmp/mysql.sock`、`.../mysqladmin --socket=/tmp/mysql.sock ping|status`
- DB 一覧: `information_schema`, `mysql`, `niac`, `performance_schema`, `test`
- **再起動手順**: `launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.mariadb.plist` → mysqld 完全終了を確認 → `launchctl load ...`。シャットダウンが滞留スレッド `did not exit` でハングする場合は `kill -9 <mysqldのPID>`（InnoDB はクラッシュセーフ、次回起動でリカバリ）→ 古い `/tmp/mysql.sock`・pid ファイルを削除してから load
- **2026-05-18 接続枯渇障害**: 3306 が外部公開状態で外部スキャナ/bot により全接続枠が CLOSE_WAIT で枯渇し "Too many connections" でダウン同然に。bind-address + pf 3306 遮断 + bot の blacklist 追加で恒久対応。詳細は `worklog_2026-05.txt`

### ファイアウォール (pf) と自動ブラックリスト

pf 有効、`/etc/pf.anchors/blacklist` アンカーで mail/SSH/VNC アクセス制御。

**起動時の自動有効化（重要）**: macOS標準の `/etc/pf.conf` は pf を自動有効化しない（Apple仕様）。再起動後も pf を有効に保つため `/Library/LaunchDaemons/com.norizuki.pf.plist`（Label `com.norizuki.pf`, RunAtLoad）が boot 時に `pfctl -E -f /etc/pf.anchors/blacklist` を実行する。**このLaunchDaemonが無いと再起動でpfがOFFになり全ポート露出する**（→ 2026-06-23 障害の根本原因。下記参照）。ログ: `/var/log/norizuki-pf.log`。
  - 手動再有効化: `sudo pfctl -E -f /etc/pf.anchors/blacklist`
  - 状態確認: `sudo pfctl -si | head -1`（`Status: Enabled` を確認）

> **2026-06-23 VNC接続不能障害**: 2026-05-02再構築後、pf自動起動の配線（上記LaunchDaemon）が欠落しpfが**Disabledのまま**だった。5900(VNC)が全世界露出し外部スキャンbotのCLOSE_WAITが33件滞留→screensharingdのセッション枠枯渇→正規VNC接続不能に（2026-05-18の3306枯渇と同型）。対応: ①`launchctl kickstart -k system/com.apple.screensharing` でscreensharingd再起動（即時復旧）②pf再有効化 ③`com.norizuki.pf` LaunchDaemon新規作成で恒久化。詳細は `worklog_2026-06.txt`。

- `/etc/pf.anchors/whitelist.txt`: 信頼IP（10.0.1.0/24, 127.0.0.0/8, 159.28.108.1作業Mac, 221.170.61.15自宅Mac）
- `/etc/pf.anchors/blacklist.txt`: 攻撃IPリスト（自動更新）
- ルール:
  - whitelist は無条件 pass
  - 22(SSH), 110(POP3), 143(IMAP plain), 445(SMB), 3306(MySQL), 5900(VNC) は whitelist 以外をブロック（3306 は 2026-05-18 追加）
  - 25, 80, 443, 465, 587, 993, 995 は誰でも接続可（受信用）
  - blacklist 該当IPは全ポート遮断

**自動ブラックリスト** (`/usr/local/bin/auth-fail-blacklist.sh` + LaunchDaemon `com.norizuki.auth-blacklist`):
- 監視: dovecot.log（tail -F）+ smtpd/sshd/screensharingd（log stream）
- 認証失敗 **2回**で対象IPを `/etc/pf.anchors/blacklist.txt` に追加 + `pfctl -t blacklist -T add`
- 失敗カウント保存: `/var/run/auth-fail-counts.txt`
- ログ: `/var/log/auth-blacklist.log`
- whitelist IPは加算対象外

## 既知の課題・制約

### NURO アウトバウンドポート25ブロック (OP25B)
- ISP（NURO Biz）が **アウトバウンドの25番をブロック**（迷惑メール対策、固定IPでも適用）
- 詳細: `nuro_smtp_op25b.txt` 参照
- 対応: 上記の Gmail SMTP リレー経由で解決済み

### PTR 逆引き
- `ip9f1c6c02.ap.nuro.jp.` のAレコードがNXDOMAIN
- NURO側でのPTR変更申請が困難なため現状維持
- DKIM署名は機能しているが、PTR問題により多くの受信側でスパム判定/拒否される

### 自己署名→Let's Encrypt 移行済み
- 一時的に自己署名（`/usr/local/etc/dovecot/ssl/dovecot.crt`）使用後、LE取得済み
- 自動更新: `/usr/local/bin/renew-le.sh` を launchd で毎日03:30実行

## SIP (System Integrity Protection)
- `/usr/libexec/postfix/` など `/usr/` 以下への書き込み不可
- メールはCatalina標準のPostfixを使うため問題なし

## as/（accounting system）固有ルール

`/Users/niac/web/norizuki.com/as/`（ローカル: `/Users/menou/Dropbox/www/norizuki.com/as/`）配下に限り、**ローカルテスト環境がないため修正後は自動デプロイを許可**。
- 適用範囲: `as/` 直下のファイルおよびサブディレクトリ全て
- 動作: ファイル編集（コード修正承認済み）後は、デプロイ承認確認をスキップして直ちに `scp` で対応するサーバーパスへ転送する
- 通常の `/Users/menou/CLAUDE.md` のデプロイ承認ルールはこのディレクトリ内では適用されない（コード修正の承認自体は引き続き必要）

## TODO（後日対応）
1. SSH 鍵 (`norizuki_rsa`) を `niac` の authorized_keys に再配置
2. SMTPリレー設定（外部送信を成立させるため）
3. yokosawa.cc の Web/メール構成（必要に応じて）
4. memory の `project_norizuki_server.md` は本ファイルに移管したため削除可
5. MariaDB に `max_connections` 明示設定なし（デフォルト151）。外部遮断済みのため緊急性は低いが、必要なら my.cnf に追記検討
