# CLAUDE.md — WordPress Helm Chart

## リポジトリ概要

Raspberry Pi などのベアメタルで稼働することを想定した、Kubernetes 上で動作する Alpine Nginx + WordPress (PHP-FPM) の Helm チャート。

- **Gitea リポジトリ**: `ssh://git@192.168.9.65/helmchart/wordpress`
- **Helm リポジトリ**: `https://git.cafepieters.com/api/packages/helmchart/helm`

## 実行環境について

WordPress本体はイメージ内蔵のもの（`docker.io/wordpress:<tag>-fpm-alpine`）をemptyDirにコピーして使い捨てで起動し、`wp-content`のみPVCで永続化する（bitnami方式）。PHPの実行環境はこのイメージに依存し、本リポジトリにPHPコードは含まれない。

## Git コミット情報

| 項目 | 値 |
|------|-----|
| 名前 | Claude |
| メール | claude@cafepieters.com |
| SSH キー | `P:\Claude\.ssh\id_claude` |

## 作業完了のルール（重要）

1. **機能を追加・変更した場合は、必ず README.md を更新すること。** 追加・変更した機能の説明を反映してから作業完了とする。
2. **変更は必ず Git Commit / Push まで行うこと。** 作業単位ごとにコミットし、origin(main) へ push するまでが作業完了。
3. **Commit メッセージに Claude クレジット（Co-Authored-By 等）を追記することは禁止。** `user.name = Claude` でコミットされるため、ユーザー名で判別可能。
4. `git pull` で取り込まれた変更は必ず尊重し、revert して push することは禁止（既存の共通ルールと同じ）。

## クラスタ構成メモ（実運用環境）

- ノード: `cpi11`(control-plane, 192.168.9.9/物理IF)、`wpi21`/`wpi22`/`wpi23`(worker)。全てRaspberry Pi(Ubuntu 25.04)。
- CNI: **Cilium**（kube-proxyなし、eBPFベース）。`cilium-envoy` Podが動作しており、Cilium組み込みIngress Controller（Envoyベース）が利用可能。
- Pod CIDR: `10.0.0.0/8`系、ノードごとに `/24` を割り当て（例: cpi11→`10.0.0.0/24`、wpi22→`10.0.1.0/24`、wpi21→`10.0.2.0/24`、wpi23→`10.0.3.0/24`）。
- 外部公開: **kube-vip**（ARP方式、静的Pod `kube-vip-cpi11`）。物理ルーターのグローバルIP配下、`192.168.9.0/24` セグメント内でVIPを払い出す。このセグメントはルーターがLAN機器(スマホ/PC等)にDHCP払い出しする範囲と重複しないよう、ルーター側で範囲を絞って共存させている。**IPが希少なため濫用できない。**
- 各WordPress/PHPFPMリリースは現状、`service.type: ClusterIP` + `kube-vip.io/loadbalancerIPs`/`kube-vip.io/vipHost` アノテーションを**Helm管理外で手動付与**し、リリースごとに個別のVIPを得る運用になっている（本チャートの `templates/service.yaml` にはannotationsのテンプレート化自体が存在しないため）。
  - **既知の問題**: この構成では、kube-vipがL2 ARP応答に加えて自前でTCPコネクションをプロキシしてしまうため、`externalTrafficPolicy`（LoadBalancer/NodePort専用でありClusterIPには無効という制約とは別に、そもそもkube-vipの時点で）実クライアントIPが失われる。詳細はREADME.mdの「kube-vip等でグローバルIP/VIPが限られている環境での実IP取得（共有Ingress Controller構成）」を参照。
  - 今後、実IP取得のためCilium組み込みIngress Controller（またはingress-nginx）へ移行する方針で合意済み（2026-08-08時点、ユーザーが選択）。ただし全リリース（wordpress-nginx系だけでなくphpfpm系も含む）の移行が必要な大掛かりな作業のため、本セッションではWordPressチャート側の対応（`ingress.yaml`/`forwardRealIP`は既存機能で対応済み、README migration手順を追記）のみ実施。実際のCilium Ingress有効化・各リリースのhelm upgradeはユーザー側の作業。

## チャート改修履歴

### 実IP取得の修正 + SMTP経由メール送信の追加（2026-08-08, v7.0.2-a）

**背景**: ルーター（グローバルIP）配下の 192.168.9.x セグメントにコントロールプレーン/ワーカーノードがあり、その上のPodでWordPressが稼働する構成において、(1) 訪問者の実IPアドレスが正しく取得できない、(2) `wp_mail()` によるメール送信が失敗する、という2つの不具合が報告された。

**(1) 実IPアドレス取得の不具合**

原因は2つ複合していた:
- Service（`LoadBalancer`/MetalLB想定）はデフォルトの `externalTrafficPolicy: Cluster` のため、kube-proxyが送信元IPをノード内部IPにSNATしてしまい、Nginxの `$remote_addr` が実IPではなくノードIPになっていた。
- 本チャートの `nginx.forwardRealIP`（X-Forwarded-Forを信頼するreal_ipモジュール設定）は、Ingress ControllerやCDNなど**XFFヘッダーを付与するリバースプロキシが前段にある場合**にのみ機能する設計だが、このベアメタル構成ではService直下にPodがぶら下がるだけでXFFを付与する層が存在せず、機能していなかった（`trustedProxies` に `192.168.0.0/16` が含まれるためSNAT後のノードIPを「信頼済みプロキシ」とみなしてしまうが、そのIPがXFFを付与するわけではないので `$real_ip` は結局SNAT後のIPのままになる）。

さらに、`wp-config.php` 生成テンプレート内でPHP側が生のX-Forwarded-Forヘッダーを無条件に信頼してREMOTE_ADDRを上書きする処理があり、Nginxのreal_ip信頼境界を迂回してクライアントがIPを詐称できる状態だった（副次的なセキュリティ上の問題）。

**修正内容**:
- `values.yaml`: `service.externalTrafficPolicy: Local` をデフォルト追加（SNATを回避し実IPをそのまま透過させる。MetalLB L2モード等と組み合わせる想定。ClusterIPでは無視される）。
- `templates/service.yaml`: `externalTrafficPolicy` を条件付きで出力するよう変更。
- `templates/deployment.yaml`: `wp-config.php` 内のPHP側REMOTE_ADDR再判定処理を削除（NginxのfastcgiパラメータでREMOTE_ADDRは既に正しく渡っているため不要かつ危険だった）。
- `values.yaml`/`README.md`: `nginx.forwardRealIP` は本当にXFFを付与するリバースプロキシが前段にある場合のみ有効化する旨を明記。

**(2) メール送信の不具合**

原因: 使用しているAlpineベースのWordPressイメージには、PHPの `mail()`（`wp_mail()` が内部的に利用）を実際に配送するMTA（sendmail相当）が同梱されておらず、`sendmail_path` の送信先が存在しなかった。

**修正内容**（[helmchart/phpfpm](https://git.cafepieters.com/helmchart/phpfpm) チャートと同一の手法を採用。ユーザー指定によりphpfpmの実装パターンをそのまま踏襲）:
- `values.yaml` に `smtp.*` セクションを追加（`enabled`/`host`/`protocol`/`port`/`auth.*`/`from`/`tls.*`）。
- `templates/configmap-smtp.yaml`（新規）: `/etc/msmtprc` を生成するConfigMap。
- `templates/secret-smtp.yaml`（新規）: SMTPパスワードを格納するSecret。
- `templates/deployment.yaml`: `smtp.enabled: true` の場合のみ、`wordpress` コンテナの起動コマンドをシェルラッパーに変更し、`apk add msmtp ca-certificates` → `/etc/msmtprc` 配置（`chmod 644`、www-dataから読めるように） → PHPの `sendmail_path` を `msmtp -t` に向ける `99-smtp.ini` を生成 → `exec php-fpm` という順で起動する。
  - `apk add` にrootが必要なため、SMTP有効時のみ既存の `securityContext: runAsUser/runAsGroup: 82` を外している（PHP-FPMワーカー自体はイメージ標準の `www.conf` によりuid82で動作するため、実際のPHPコード実行権限は変わらない）。`smtp.enabled: false`（デフォルト）では従来どおり非rootで起動する。
  - msmtpのパスワードはmsmtprcに直書きせず `passwordeval "cat /etc/smtp-secrets/password"` で別ファイル参照（phpfpmの `CHANGELOG-8.5.6-f/g.md` で判明した「Secret/ConfigMapの権限が0600だとwww-dataから読めない」問題を踏まえ、最初から `0644` で実装済み）。
- WordPress本体は `wp_mail()` が自動的に `mail()`/`sendmail_path` を経由するため、phpfpmと異なりPHPコード側の変更（ヘルパークラスの読み込み等）は不要。

**対象ファイル**: `values.yaml`, `templates/service.yaml`, `templates/deployment.yaml`, `templates/configmap-smtp.yaml`（新規）, `templates/secret-smtp.yaml`（新規）, `README.md`, `Chart.yaml`
