From be07580803eec4fd068a68aee297c68af269e3b1 Mon Sep 17 00:00:00 2001 From: Ruk Doe Date: Sun, 18 Oct 2026 14:57:31 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20=E7=B5=8C=E7=B7=AF=E3=81=A8=E9=87=8D?= =?UTF-8?q?=E8=A4=87=E3=82=92=E5=89=8A=E3=81=A3=E3=81=A6=E7=9F=AD=E3=81=8F?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 日付つきの経緯・移行の話(k3s / Ubuntu の頃、Zulip・wg-easy・Traefik・Portainer など)を削った。 再発しうる罠だけ 1 行で残した - 同じ説明が 2 か所にあるものは片方を短いリンクにした(公開経路、DNS の方針、fj.doany.io の取得、bootstrap の層の分け方など) - 終わった作業の記録(ExternalDNS の移行表、pulumi の経緯、k8up の retired タグ、ROADMAP の済んだこと)を圧縮・削除 - docs/entra.md の wg-easy のアプリ登録の行を削除(Entra に既に無い) - forgejo の README のリダイレクト URI を pulumi/entra.ts を指す形に直した Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01JbmWyUB9jcBcYCnNyiGBSV --- README.md | 7 +- ROADMAP.md | 25 ++----- apps/external-dns/README.md | 15 +--- apps/forgejo/README.md | 38 ++++------ apps/k8up/README.md | 8 +-- apps/matrix/README.md | 27 +++----- apps/mta-sts/README.md | 5 +- apps/renovate/README.md | 8 +-- apps/rybbit/README.md | 7 +- bootstrap/README.md | 21 ++---- docs/cloudflare-tokens.md | 4 +- docs/decisions.md | 134 ++++++++++++++---------------------- docs/entra.md | 14 ++-- docs/router.md | 18 +++-- docs/talos.md | 20 ++---- docs/z440-migration.md | 8 +-- mado/README.md | 11 ++- pulumi/README.md | 30 +++----- recovery/README.md | 8 +-- talos/README.md | 7 +- tools/README.md | 17 ++--- 21 files changed, 154 insertions(+), 278 deletions(-) diff --git a/README.md b/README.md index cedf2aa..4da9dc0 100644 --- a/README.md +++ b/README.md @@ -74,20 +74,19 @@ headlamp が 1 文字なのは、規則どおりだと head + lamp でも hubble | [`cloudflare-ddns/`](apps/cloudflare-ddns/) | DDNS | | [`external-dns/`](apps/external-dns/) | ExternalDNS。**Cloudflare を通すアプリの DNS を、アプリの HTTPRoute の注釈から作る**(gitops には書かない)。直接つなぐアプリはワイルドカードで引ける | | [`mta-sts/`](apps/mta-sts/) | MTA-STS の方針ファイル(`mta-sts.doany.io`)。DANE・TLS-RPT と合わせて、doany.io 宛てのメールのサーバー間の TLS を守る | -| [`matrix/`](apps/matrix/) | Matrix のサーバー一式(`m.doany.io`。Web 版の Element は `e.doany.io`、スマホと PC は公式のアプリ)。Tuwunel + hookshot(通知の受け口)+ LiveKit(通話。`lk.doany.io`、hostPort 8443 をルーターで転送)。**Zulip からの移行先。使い始めは [apps/matrix/README.md](apps/matrix/README.md)** | +| [`matrix/`](apps/matrix/) | Matrix のサーバー一式(`m.doany.io`。Web 版の Element は `e.doany.io`、スマホと PC は公式のアプリ)。Tuwunel + hookshot(通知の受け口)+ LiveKit(通話。`lk.doany.io`、hostPort 8443 をルーターで転送)。**使い始めは [apps/matrix/README.md](apps/matrix/README.md)** | | [`forgejo/`](apps/forgejo/) | Forgejo(`fj.doany.io`、git + Actions)+ PostgreSQL + Runner(dind)。GitHub Actions の課金を避けて CI をここで回すため。**使い始めの手順は [apps/forgejo/README.md](apps/forgejo/README.md)** | | [`netbird/`](apps/netbird/) | NetBird (VPN。combined コンテナ + 内蔵 IdP) | | [`3proxy/`](apps/3proxy/) | 3proxy (国内IP経由の HTTPS フォワードプロキシ) | | [`k8up/`](apps/k8up/) | バックアップ(restic → Cloudflare R2)。PVC・DB のダンプ・etcd のスナップショット | | [`infisical/`](apps/infisical/) [`infisical-operator/`](apps/infisical-operator/) [`infisical-push-bridge/`](apps/infisical-push-bridge/) | 秘密の配布。本体は `bootstrap/` にあり、ここには公開経路と operator と即時反映のブリッジ | | [`renovate/`](apps/renovate/) | Renovate(GitHub の danything・5ym と Forgejo の doa の全リポジトリの依存を上げる PR)。CronJob で 15 分おき。**秘密の入れ方は [apps/renovate/README.md](apps/renovate/README.md)** | -| [`headlamp/`](apps/headlamp/) | Kubernetes の Web UI(`h.doany.io`、Portainer の置き換え)。認証は Entra、権限は `bootstrap/apiserver/` | +| [`headlamp/`](apps/headlamp/) | Kubernetes の Web UI(`h.doany.io`)。認証は Entra、権限は `bootstrap/apiserver/` | | [`rybbit/`](apps/rybbit/) | Rybbit(`rt.doany.io`、アクセス解析。クッキー無し)+ ClickHouse + PostgreSQL + Redis。トドロクの離脱をファネル・ジャーニー・セッションリプレイで見る。**使い始めは [apps/rybbit/README.md](apps/rybbit/README.md)** | **動かしていないもの**は `apps/_/` に置いてある。`_` で始まるディレクトリは Argo CD が同期しない ([bootstrap/argocd/repos.yaml](bootstrap/argocd/repos.yaml) の `directory.exclude`。全リポジトリ共通の決まり)ので、 -クラスタには何も作らない。動かすときは `git mv apps/_ apps/`。いまは無い -(GlitchTip を置いていたが、動かさないまま 2026-10-17 に消した。戻すなら git の履歴から)。 +クラスタには何も作らない。動かすときは `git mv apps/_ apps/`。いまは無い。 **アプリの多くはこのリポジトリに無い。** 各アプリのリポジトリの `deploy/` に置いてあり、 `bootstrap/argocd/repos.yaml` の ApplicationSet が拾う(上の「仕組み」)。 diff --git a/ROADMAP.md b/ROADMAP.md index 90c1e49..a713a34 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -5,12 +5,9 @@ ## いまここ -**2026-10-17 に DL360 の上で Ubuntu + k3s から Talos(v1.15.0-alpha.1 / Kubernetes v1.37.1)に移した。** -git から建て直して restic から戻し、全部のアプリが動いている。**次は Phase 2.5(Talos のまま HP Z440 へ移す)。** +**DL360 の上の Talos で全部のアプリが動いている。次は Phase 2.5(Talos のまま HP Z440 へ移す)。** 順番の理由は [docs/decisions.md](docs/decisions.md)「Z440 へは Talos のまま移す」。 -**実機の RAM は 2026-10-17 から RDIMM 6 枚中 5 枚(40GB)。** 1 枚は Z440 の BIOS 更新に貸している。 - ## Phase 2.5 — HP Z440 へ移す(Talos のまま) **手順と確かめることは [docs/z440-migration.md](docs/z440-migration.md)。** 箱の違いは schematic と NIC の名前に閉じ込め、 @@ -23,25 +20,13 @@ bond0 の MAC は設定で固定してあるのでルーターは触らない。 ## Phase 3 — Talos 定常運用 -- [ ] **R2 の容量が下がり切るのを見る。** `host=main` を消した直後の restic の実データは **5.60 GiB**(圧縮前 8.52 GiB、 - 480 スナップショット。2026-10-17)。denpa の録画の forget([apps/k8up/denpa-encoded-forget.yaml](apps/k8up/denpa-encoded-forget.yaml))は - ホストのスナップショットが同じ中身を抱えていたので効いていなかった。日曜の prune のあと、`pulumi` ワークフローの週次の Summary で見る +- [ ] **R2 の容量が下がり切るのを見る。** `host=main` を消した直後の restic の実データは **5.60 GiB**(2026-10-17)。 + 日曜の prune のあと、`pulumi` ワークフローの週次の Summary で見る - [ ] **Talos 1.15.0 の正式版が出たら乗り換える**(Renovate の PR)。PT3 のモジュールの読み順を守る上流の修正 ([siderolabs/talos#14652](https://github.com/siderolabs/talos/pull/14652))が入ったら、[apps/pt3/rebind.yaml](apps/pt3/rebind.yaml) を外して `talos/patches/main.yaml` の並び順だけにする。入ったら dvb-pt3 の extension の README も直す(上流) ## 済んだこと(記録) -1 行ずつ。詳しいことはリンク先にある。 - -- **Talos へ移した(2026-10-17)。** git から建て直し、PV を restic から戻した。踏んだ穴は直した場所と [recovery/README.md](recovery/README.md) に -- **後片付け(2026-10-17)。** Ubuntu 期のホストのスナップショット(`host=main` 10 本)を forget + prune。etcd の日次スナップショットを - R2 へ([apps/k8up/etcd-snapshot.yaml](apps/k8up/etcd-snapshot.yaml)、最初の 1 本 `dacc947b` を手で回して確認) -- **四半期ごとの手動の復元リハーサルをやめて自動に(2026-10-17)。** データは月次の CronJob が最新のスナップショットを取り出して開く - ([apps/k8up/restore-check.yaml](apps/k8up/restore-check.yaml)。SQLite の integrity_check、pg_dump を使い捨ての postgres に流す、tar の中身。 - 初回 27 件すべて OK)。ホストの設定は [.github/workflows/talos-boot.yml](.github/workflows/talos-boot.yml) が描いた machine config を QEMU で起動する -- **バックアップを restic + k8up に(2026-09-06〜09-08)。** R2 の `doany-restic` へ日次、論理バックアップと PVC の注釈、失敗通知、`restic check`([apps/k8up/README.md](apps/k8up/README.md))。R2 のずれ検知(2026-10-05) -- **Cilium + Gateway API に(2026-09-06〜07)。** **Talos で後から替えるのが高いのは CNI だけ**なので先に。decisions.md「ルーティングの選定」 -- **Talos の検証(2026-09-06〜10-17)。** QEMU で 7 回、いまは PR ごとに自動([docs/talos.md](docs/talos.md)「VM ブートドリル」) -- **運用まわり(2026-09-07〜)。** Cilium の値を git に、Entra をアプリロールに([docs/entra.md](docs/entra.md))、自動マージの範囲(decisions.md「依存の更新をどこまで自動で入れるか」)、Talos の更新は検知だけ自動([talos/versions.yaml](talos/versions.yaml))、通信の可視化は Hubble(`hl.doany.io`) -- **PVC を守る仕掛けをやめた(2026-09-07、`local-path-retain` の撤去は 2026-10-05)** ── decisions.md「PVC を守る仕掛けをやめた」 +- **Talos へ移した(2026-10-17)。** git から建て直し、PV を restic から戻した([recovery/README.md](recovery/README.md)) +- **バックアップと復元の確認を自動に。** restic + k8up で日次、etcd も日次、月次の取り出し確認、machine config は PR ごとに QEMU で起動([apps/k8up/README.md](apps/k8up/README.md)) diff --git a/apps/external-dns/README.md b/apps/external-dns/README.md index b61bf1b..1ebf998 100644 --- a/apps/external-dns/README.md +++ b/apps/external-dns/README.md @@ -1,6 +1,6 @@ # external-dns -**Cloudflare を通すアプリの DNS レコードを、アプリの HTTPRoute から作る**([ExternalDNS](https://github.com/kubernetes-sigs/external-dns)。2026-10-06)。 +**Cloudflare を通すアプリの DNS レコードを、アプリの HTTPRoute から作る**([ExternalDNS](https://github.com/kubernetes-sigs/external-dns))。 ## 方針 @@ -33,16 +33,3 @@ ExternalDNS は**自分が作ったレコードにしか触れない**。作っ Cloudflare の Account API token「external-dns (doany.io DNS write)」。権限は **`doany.io` のゾーンだけの Zone Read + DNS Write**。 Infisical `/external-dns/external-dns` の `cloudflare-api-token`。 -## 移した記録 - -| ホスト | アプリ | 移した日 | -| --- | --- | --- | -| `tk.doany.io` | todoroku | 2026-10-06(todoroku#343) | -| `l.doany.io` | lgtm | 2026-10-06(lgtm#61) | -| `x.doany.io` | xool | 2026-10-06(xool#164) | -| `y.doany.io` | yuzuriha | 2026-10-06(yuzuriha#20) | -| `ts.doany.io` | tamasagashi | 2026-10-06(tamasagashi#100) | -| `w.doany.io` | worklog | 2026-10-06(worklog-cloud#143) | - -**手で作った proxied の CNAME はもう無い。** Cloudflare を通すアプリを足すときは、アプリのリポジトリの HTTPRoute に注釈を付けるだけでよい。 - diff --git a/apps/forgejo/README.md b/apps/forgejo/README.md index 3cd606a..f724cdd 100644 --- a/apps/forgejo/README.md +++ b/apps/forgejo/README.md @@ -19,7 +19,7 @@ - `/forgejo/forgejo-db`: `postgres-password` ── 英数字だけのランダム(`openssl rand -hex 32` など)。 postgres は最初の起動でしかパスワードを設定しないので、**後から変えるなら DB 側も変える** - `/forgejo/forgejo-oauth`: `key` = `b0fa498f-7e6a-4fe1-a1c6-16fbbb6f397e`(Main のクライアント ID。秘密ではなく bootstrap/auth にも平文で書いてある)、`secret` = `${prod.auth.auth-secrets.oidc-client-secret}`(値は写さず参照) -2. Entra のアプリ登録 Main のリダイレクト URI(Web)に `https://fj.doany.io/user/oauth2/entra/callback` ── **2026-09-15 に `az ad app update` で追加済み** +2. Entra のアプリ登録 Main のリダイレクト URI に `https://fj.doany.io/user/oauth2/entra/callback`([pulumi/entra.ts](../../pulumi/entra.ts) にある) 3. main にマージ → ArgoCD が同期 4. **管理者を作る**(1 度だけ)。Entra のメールと同じにしておくと、Entra でログインしたときに自動で紐づいて管理者になる。 パスワードは捨てる(表示させない)。組織 `doa` とチーム `members`(読み取り・すべてのリポジトリ)も作る @@ -39,23 +39,15 @@ ## プライベートのリポジトリを移す(1 本ずつ) -**方針: プライベートは GitHub に置かない**(2026-09-15)。GitHub Actions の課金はプライベートにだけ掛かるので、 -公開リポジトリ(gitops・blog など)は GitHub のままでよい。対象は todoroku / tamasagashi / worklog-cloud(noren は閉じた)。 +**方針: プライベートは GitHub に置かない。** GitHub Actions の課金はプライベートにだけ掛かるので、 +公開リポジトリ(gitops・blog など)は GitHub のままでよい。 **Forgejo が唯一の置き場になる。** バックアップは k8up(R2)の 1 日 1 回だけなので、手元の clone も捨てないこと。 -先に 1 回だけ: - -1. Forgejo に組織 `doa` を作る -2. ArgoCD 用のアクセストークンを作り(`read:repository` と `read:organization`)、Infisical `/argocd/forgejo-repo-creds` に - `url` = `https://fj.doany.io/doa` / `username` / `password` = トークン で入れる([argocd-creds.yaml](argocd-creds.yaml))。 - **スコープは repository・organization・issue の読み取り。** issue が無いと、非公開リポジトリができた時点で - ApplicationSet が `token does not have at least one of required scope(s): [read:issue]` で止まる(2026-09-15) -3. `bootstrap/argocd/repos.yaml` に Forgejo の generator を足す PR をマージする(**Forgejo とトークンが揃ってから**。 - API に届かないと ApplicationSet 全体の生成が止まる)。 - **2026-10-05 に `repos-forgejo` という別の ApplicationSet に分けた。** 同居していると、まっさらな - クラスタ(Forgejo が空で、トークンもまだ無い)で GitHub 側の Application まで 1 本も作られず、 - Forgejo 自身もデプロイされないので抜けられなかった。いまは止まるのは Forgejo 側だけ +ArgoCD が読むトークンは Infisical `/argocd/forgejo-repo-creds`([argocd-creds.yaml](argocd-creds.yaml))。 +**スコープは repository・organization・issue の読み取り。** issue が無いと ApplicationSet が +`token does not have at least one of required scope(s): [read:issue]` で止まる。 +ApplicationSet は GitHub 側と別の `repos-forgejo`(同居させると、Forgejo が空のクラスタで GitHub 側まで 1 本も作られない)。 リポジトリごとに: @@ -78,7 +70,7 @@ | テナントの人(Entra、チーム members) | 見える | 読める | できる | できない | ─ | | 管理者(Forgejo の管理画面で付ける) | 全部 | 全部 | できる | できる | できる | -- **テナントのゲスト(もとは ERPNext のために招いた人)も tid が同じなので members に入る。** 外したくなったら Forgejo 専用のアプリ登録を作り、 +- **テナントのゲストも tid が同じなので members に入る。** 外したくなったら Forgejo 専用のアプリ登録を作り、 「割り当てが必要」にして人ごとに割り当てる(Main の設定を変えると、ロールを割り当てていない人が Main の後ろのアプリ全部から締め出される。 [../../docs/entra.md](../../docs/entra.md)) - **Entra のアプリロール admin は Forgejo の管理者に連動しない**(グループのクレームを tid に使っているため)。管理者は管理画面で付け外しする @@ -95,7 +87,7 @@ **公開にしてはいけない。** パッケージ(コンテナイメージ)の読み取り権限はリポジトリではなく**組織の公開範囲**で決まり、 公開の組織だと非公開リポジトリのイメージでも匿名で pull できる(`services/packages/perm.go` の -`HasOrgOrUserVisible`。2026-09-15 に shadai(今の todoroku)のイメージが匿名で取れるのを見つけて limited にした)。 +`HasOrgOrUserVisible`)。 ## Actions からイメージを push するとき @@ -124,18 +116,18 @@ CI から Forgejo の API・git・レジストリに**長く生きる秘密な - **リポジトリを絞ると**、スコープは repository / issue しか選べず、管理者の権限は効かない(`services/authz/access_token.go`)。 レジストリ(package)と組織の操作は「すべて」で作る - **Forgejo Actions のジョブは `enable-openid-connect: true`** で JWT を取る。fork からの PR では取れない。 - JWT の `workflow` は `name:` ではなく**ファイル名**(`run.WorkflowID`。2026-10-07 に実ジョブの JWT で確かめた。GitHub とは違う)。 + JWT の `workflow` は `name:` ではなく**ファイル名**(`run.WorkflowID`。GitHub とは違う)。 再利用ワークフロー(`uses: ./...`)のジョブは、**呼ばれる側**のファイルの設定が効き、JWT の `workflow` は**呼ぶ側**のファイル名になる - **境界は main に書ける人**: main のワークフローを書き換えられる人は、その JWT で持ち主として書ける(シークレットのときと同じ)。 doa の main に書けるのは bots と Owners だけ(members は読み取り) | 状態 | 持ち主 | 名前 | 発行者 | クレームのルール | 範囲・スコープ | aud の置き場 | 代わりに消したもの | | --- | --- | --- | --- | --- | --- | --- | --- | -| **使っている**(2026-10-07) | yui | repo-config | GitHub Actions | GitHub の 5ym/repo-config、`refs/heads/main`、`forgejo-settings.yml`、push / schedule / workflow_dispatch | すべて・`write:organization` `write:repository` | GitHub の 5ym/repo-config の変数 `FORGEJO_AUDIENCE` | repo-config の secrets `FORGEJO_TOKEN` と yui のトークン `repo-config-settings` | +| **使っている** | yui | repo-config | GitHub Actions | GitHub の 5ym/repo-config、`refs/heads/main`、`forgejo-settings.yml`、push / schedule / workflow_dispatch | すべて・`write:organization` `write:repository` | GitHub の 5ym/repo-config の変数 `FORGEJO_AUDIENCE` | repo-config の secrets `FORGEJO_TOKEN` と yui のトークン `repo-config-settings` | | 見送り | forgejo-bot | doa-registry | Forgejo Actions | doa の todoroku・tamasagashi・worklog-cloud、`refs/heads/main`、`publish.yml` / `fetch-latest.yml`、push / workflow_dispatch / schedule | すべて・`write:package` | 組織 doa の変数 `REGISTRY_AUDIENCE` | (今は組織の `REGISTRY_TOKEN`) | | 見送り | forgejo-bot | todoroku-deploy | Forgejo Actions | todoroku、`refs/heads/main`、`publish.yml`、push / workflow_dispatch | todoroku だけ・`write:repository` | todoroku の変数 `DEPLOY_AUDIENCE` | (今は todoroku の `DEPLOY_TOKEN`) | -**forgejo-bot の 2 つは見送った**(2026-10-07)。forgejo-bot のものは 16.0.5 では画面にも CLI・API にも一覧・変更・削除の手段が無く +**forgejo-bot の 2 つは見送った。**forgejo-bot のものは 16.0.5 では画面にも CLI・API にも一覧・変更・削除の手段が無く (forgejo-bot は画面にログインできない。`ENABLE_INTERNAL_SIGNIN: "false"`)、止めるには DB の `authorized_integration` の行を触ることになる。 v17 以降で管理の手段が揃ったら、表のルールで `forgejo admin user create-authorized-integration --username forgejo-bot …` を本番で打ち、 出力の `audience`(`u:<ユーザー ID>:`。秘密ではない)を表の変数に入れて、ワークフローの PR @@ -147,9 +139,7 @@ yui のもの(repo-config)は画面(設定 → 承認済みインテグレーシ ## ノードからイメージを取る -ノードの名前解決で fj.doany.io をノード自身の Gateway に向ける(`10.0.0.2 fj.doany.io`。`talos/patches/cluster.yaml` の -`StaticHostConfig`。公開 DNS の A にはノードから届かない)+ `talos/registries.yaml` の資格情報 -(yui の `read:package`、`talos-registry-pull`)。アプリ側に `imagePullSecrets` は要らない([../../talos/README.md](../../talos/README.md)「fj.doany.io の資格情報」)。 +アプリ側に `imagePullSecrets` は要らない([../../talos/README.md](../../talos/README.md)「fj.doany.io の資格情報」)。 ## マージはスカッシュ @@ -161,5 +151,3 @@ curl -X PATCH -H "Authorization: token $T" -H 'Content-Type: application/json' \ -d '{"allow_merge_commits":false,"allow_rebase":false,"allow_rebase_explicit":false,"allow_fast_forward_only_merge":false,"allow_squash_merge":true,"default_merge_style":"squash","default_delete_branch_after_merge":true}' \ https://fj.doany.io/api/v1/repos/doa/ ``` - -2026-09-15 に shadai(今の todoroku)/ renovate / tamasagashi / worklog-cloud へ当てた。 diff --git a/apps/k8up/README.md b/apps/k8up/README.md index 7d7de86..6c5df49 100644 --- a/apps/k8up/README.md +++ b/apps/k8up/README.md @@ -82,8 +82,8 @@ restic は全部 `--no-lock`。OOMKilled や期限切れで投稿まで届かな ## 空のスナップショット **k8up のバックアップ Pod は既定で nonroot(65532)で走り、`0600 root:root` のファイルが読めない。** restic は -`errors: N` を出しながら 0 バイトのスナップショットを作り、**k8up はそれを `Succeeded` にする** -(2026-09-08 に adguardhome の 2 本が何週間もこの状態だった)。`0640` はグループ読みで通る。 +`errors: N` を出しながら 0 バイトのスナップショットを作り、**k8up はそれを `Succeeded` にする**。 +`0640` はグループ読みで通る。 Schedule 全部に `podSecurityContext: {runAsUser: 0, runAsGroup: 0}` を付けて直した(PSA `baseline` でも通る。 `restricted` に寄せるなら、アプリ側がファイルを `0640` で作るようにする)。notify は `summary.total_bytes_processed == 0` @@ -108,7 +108,3 @@ notify は過去 8 日に出てきた (host, path) を「あるべきもの」 - 打つときは k8up のイメージ(`ghcr.io/k8up-io/k8up`、restic 入り)と `k8up-global` の秘密で使い捨ての Job を `kubectl -n k8up create -f -` で流す(env の渡し方は [denpa-encoded-forget.yaml](denpa-encoded-forget.yaml) と同じ) - forget はリポジトリを排他でロックするので、schedules.yaml の時刻を外す - -2026-10-18 までは残しておきたいものに `retired` のタグを付け、notify と restore-check で外していた。そのとき残っていた -shadai(todoroku の旧名)・zulip と、Talos に移す前の lgtm・xool・todoroku の 66 本は 2026-10-18 に消した -(重複排除でほとんど共有されていて、空いたのは 42 MiB)。 diff --git a/apps/matrix/README.md b/apps/matrix/README.md index b3f4568..b1e35c4 100644 --- a/apps/matrix/README.md +++ b/apps/matrix/README.md @@ -1,7 +1,6 @@ # matrix -Matrix のサーバー一式(`m.doany.io`)。**Zulip からの移行先**(2026-10-06 に移して、Zulip は畳んだ)。 -Zulip の最後のバックアップ(pg_dump と添付ファイル)は 2026-10-18 に R2 から消した。 +Matrix のサーバー一式(`m.doany.io`)。 | 名前 | 何か | ここで動くか | | --- | --- | --- | @@ -27,10 +26,7 @@ Zulip の最後のバックアップ(pg_dump と添付ファイル)は 2026-10-1 - **日本語の検索は弱い。** Tuwunel は記号と空白でしか単語を区切らないので、ひらがなや漢字の続きは ひとかたまりでしか当たらない(`src/service/rooms/search/mod.rs`)。Synapse も英語用の検索に決め打ちで (`to_tsvector('english', …)`)、日本語圏の共通の悩み。**後から見たい情報はメッセージではなくドキュメントに書く** - 前提で選んだ(本人、2026-10-06) -- **Zulip の履歴は持ってこない。** Mattermost からも Zulip からも、Matrix への公式の取り込みが無い -- **通知のルームは暗号化しない。** hookshot は暗号化したルームに書けない(Redis と暗号化の設定が要る)。 - Element は非公開のルームを既定で暗号化するので、通知のルームは作るときに暗号化を切る + 前提で選んだ(本人) ## ログイン @@ -46,19 +42,17 @@ Entra だけ(Tuwunel の `[[global.identity_provider]]`)。パスワードのロ ## 通知のルーム -**スペース「通知」**にまとめてある(2026-10-07)。部屋の名前は接頭辞を付けず短く(`server`・`argocd`・`ashi`・ -`todoroku`・`worklog`・`denpa`・`forgejo`・`feed`)。Slack のような `notify-` の接頭辞の代わりにスペースで束ねる -(名前は表示用で、hookshot も webhook も部屋の ID で動くので、変えても影響しない)。 -2026-10-06 に `notify-*` として 8 つ作り、`notify-github` は 2026-10-07 に畳んだ。Zulip のメッセージは持ってきていない(ほとんどが bot の通知のため)。 +**スペース「通知」**にまとめてある。部屋の名前は接頭辞を付けず短く(`server`・`argocd`・`ashi`・ +`todoroku`・`worklog`・`denpa`・`forgejo`・`feed`)。名前は表示用で、hookshot も webhook も部屋の ID で動くので、変えても影響しない。 - 作ったのは hookshot(`@hookshot:doany.io`)。`@info:doany.io` も作成者(room version 12 の `additional_creators`) なので、権限は最上位で外されない - **暗号化していない。** hookshot は暗号化したルームに書けない。暗号化を後から入れると戻せないので、入れないこと + (Element は非公開のルームを既定で暗号化するので、作るときに切る) - webhook は [hookshot.yaml](hookshot.yaml) の `connections`。URL は `https://m.doany.io/webhook/` で、ID は Infisical `/matrix/matrix` の `hook-<部屋>`(公開のリポジトリなので git に置かない) -- **投稿は全部 m.notice(bot の発言)**(2026-10-07)。通知と音は Element の設定で決める: +- **投稿は全部 m.notice(bot の発言)**。通知と音は Element の設定で決める: グループの会話は「音あり」、bot のメッセージは「オン」(音無しで通知)、要らない部屋は部屋ごとに「メンションのみ」。 - (以前は通知したい部屋だけ m.text で投稿していたが、グループの会話を音ありにすると bot まで鳴るのでやめた) | ルーム | 送り元 | URL の在処 | | --- | --- | --- | @@ -77,9 +71,8 @@ Entra だけ(Tuwunel の `[[global.identity_provider]]`)。パスワードのロ ## GitHub 連携(hookshot) -**使っていない**(2026-10-07)。GitHub の通知は GitHub のアプリで受ける。GitHub App `doa-hookshot` は消したので、 -hookshot の設定からも `github:` を外した。**App を消して設定を残すと、hookshot が `Integration not found` で -起動しなくなり、通知が全部止まる**(2026-10-07 に踏んだ)。また入れるなら、GitHub App を作り直して +**使っていない。** GitHub の通知は GitHub のアプリで受ける。**App を消して設定を残すと、hookshot が `Integration not found` で +起動しなくなり、通知が全部止まる。**また入れるなら、GitHub App を作り直して `github:`・秘密・`/github` の経路・`@_github_` の名前空間を戻す(git の履歴の #243 が手順)。 ## RSS @@ -88,7 +81,7 @@ hookshot の feeds で RSS / Atom を購読する(10 分おき)。購読はル ルームで `!hookshot feed ` を打つか、hookshot の bot として state `uk.half-shot.matrix-hookshot.feed`(state key と本文の `url` に URL)を書く。 -流す先はスペース「通知」の **`feed`**(2026-10-07 に作った。暗号化なし、`@info:doany.io` も作成者)。 +流す先はスペース「通知」の **`feed`**(暗号化なし、`@info:doany.io` も作成者)。 | 購読 | URL | | --- | --- | @@ -96,7 +89,7 @@ hookshot の feeds で RSS / Atom を購読する(10 分おき)。購読はル ## 通話(LiveKit) -LiveKit が Matrix の通話(Element Call)の音声と映像を中継する。2026-10-06 に入れた。手順の元は Tuwunel の +LiveKit が Matrix の通話(Element Call)の音声と映像を中継する。手順の元は Tuwunel の `docs/calls/matrix_rtc.md`。 | 部品 | 役目 | 在処 | diff --git a/apps/mta-sts/README.md b/apps/mta-sts/README.md index fd17901..d537e8d 100644 --- a/apps/mta-sts/README.md +++ b/apps/mta-sts/README.md @@ -1,7 +1,7 @@ # mta-sts doany.io 宛てのメールの、**サーバーどうしの通信を守る** 2 つの仕組み。どちらも「途中で平文に落とされる・偽の -サーバーに渡される」のを防ぐ。2026-10-07 に入れた。 +サーバーに渡される」のを防ぐ。 | 仕組み | 何で守るか | 在処 | | --- | --- | --- | @@ -14,11 +14,10 @@ doany.io 宛てのメールの、**サーバーどうしの通信を守る** 2 今は **enforce**(max_age 1 週間)。**MX を変えるときは、先に testing に戻して id を変え、1 週間待つ**(enforce のまま MX を替えると、古い方針を覚えている送り手が新しい MX に送らなくなる)。 - 1. [mta-sts.yaml](mta-sts.yaml) の `mta-sts.txt` を直し、`mta-sts/policy-rev` を上げる 2. dns.ts の `_mta-sts` の `id` を変える(今の UTC の時刻など)。**変えないと送り手は古い方針を使い続ける** -## DANE への切り替え(2026-10-07 に済んだ。Microsoft の手順。learn.microsoft.com の「How SMTP DANE works」) +## DANE への切り替え(済み。Microsoft の「How SMTP DANE works」の手順) MX を `doany-io.mail.protection.outlook.com` から、DNSSEC に対応した `…mx.microsoft` に替え、Exchange 側で DANE を 有効にする。受信を止めないよう、MX の TTL を下げてから新旧を並べて入れ替える。MTA-STS はそのあいだ testing にする。 diff --git a/apps/renovate/README.md b/apps/renovate/README.md index 3da3694..818e2f6 100644 --- a/apps/renovate/README.md +++ b/apps/renovate/README.md @@ -1,11 +1,9 @@ # renovate -Renovate を CronJob で回す(2026-10-16〜)。GitHub の `danything` と `5ym`、Forgejo(fj.doany.io)の `doa` の全リポジトリ。 +Renovate を CronJob で回す(GitHub Actions の schedule は遅れ、飛ばされることもあるため)。 +GitHub の `danything` と `5ym`、Forgejo(fj.doany.io)の `doa` の全リポジトリ。 設定は各リポジトリの `renovate.json`(共通は `github>5ym/repo-config`)。 -それまでは GitHub Actions(5ym/repo-config の `renovate.yml`)と Forgejo Actions(doa/renovate)で毎時回していたが(どちらも消した)、 -GitHub の schedule は 15〜40 分遅れ、丸ごと飛ばされることもあった。 - CronJob `renovate` が 15 分おき(毎時 2・17・32・47 分)に Job を 1 つ作り、その中の 3 つの Pod が並んで回る (Indexed Job。番号 0・1・2 が GitHub の danything・5ym、Forgejo の doa)。 @@ -33,5 +31,5 @@ GitHub のトークンは実行のたびに App の鍵から作る(1 時間で ## クラスタが落ちているとき -Renovate も止まる。予備は置かない(もとの GitHub Actions と Forgejo Actions の workflow は 2026-10-17 に消した)。 +Renovate も止まる。予備は置かない。 クラスタが止まっているときはそれを直すのが先で、ほかの依存を上げる PR を出しても仕方がない(Forgejo はそもそもクラスタの上)。戻れば次の実行で追いつく。 diff --git a/apps/rybbit/README.md b/apps/rybbit/README.md index 1b26213..b1c8407 100644 --- a/apps/rybbit/README.md +++ b/apps/rybbit/README.md @@ -2,7 +2,7 @@ アクセス解析。Google Analytics の置き換えで、クッキーを使わない(同意バナーが要らない)。 トドロク(tk.doany.io)の LP から購入までのどこで離脱するかを、ファネル・ユーザージャーニー・ -セッションリプレイで見るために置いた(2026-09-19)。AGPL-3.0、自分で動かす分には縛りはない。 +セッションリプレイで見るために置いた。AGPL-3.0、自分で動かす分には縛りはない。 上流: 。構成は上流の docker-compose.yml をそのまま k8s に写した。 @@ -24,7 +24,7 @@ 入れるまで Pod は `CreateContainerConfigError` で待つ(壊れてはいない) 2. を開き、最初のアカウントを作る(これが管理者) 3. **すぐに** [rybbit.yaml](rybbit.yaml) の `DISABLE_SIGNUP` と `NEXT_PUBLIC_DISABLE_SIGNUP` を `"true"` にして - PR を出す。false のままだと誰でもこのインスタンスにアカウントを作れる(2026-09-19 に済み。 + PR を出す。false のままだと誰でもこのインスタンスにアカウントを作れる(済み。 人を足すなら一時的に false に戻すか、画面の招待で。招待メールは Resend が無いので届かない) 4. 画面で「サイトを追加」→ `tk.doany.io`。出てくるサイト ID をトドロクの `PUBLIC_RYBBIT_SITE_ID` (todoroku の `deploy/deployment.yaml`)に入れる。トドロクの root layout が @@ -41,5 +41,4 @@ clickhouse.yaml の limit と `resource_limits.xml` は比率なので limit だけ上げればよい - **版。** GitHub のリリースの `vX.Y.Z` を 2 つのイメージに同じタグで。Renovate が PR を出す。 ClickHouse・PostgreSQL・Redis は上流の compose が使っている版に合わせる -- **PSA は baseline。** privileged にした他の namespace と違い、hostPort も特権も要らない。 - Talos に移ったときそのまま通る +- **PSA は baseline。** privileged にした他の namespace と違い、hostPort も特権も要らない diff --git a/bootstrap/README.md b/bootstrap/README.md index ae9c3b8..e155c0b 100644 --- a/bootstrap/README.md +++ b/bootstrap/README.md @@ -7,9 +7,6 @@ | **machine config**([../talos/render.sh](../talos/render.sh) が描く) | Helm で入れるもの(cilium・cert-manager・argocd・infisical)、`apiserver/rbac.yaml`、SOPS 済みの Secret 2 つ | `render.sh` → `apply-config` → `upgrade-k8s`([../talos/README.md](../talos/README.md)「上げ方 / 当て直し方」) | | **ArgoCD**(Application `bootstrap`。[argocd/repos.yaml](argocd/repos.yaml)) | 残りの平文のマニフェスト(`ClusterIssuer`・Gateway・auth・`InfisicalSecret` など) | main へのマージ(prune / selfHeal 付き) | -2026-10-17 まで 2 つめは GitHub Actions(`bootstrap-apply.yml`)だった。ArgoCD に移しても鶏と卵にならないのは、 -ArgoCD が動くのに要るもの(本体・repo の資格情報・ApplicationSet の入口 `apps`)が全部 machine config にあり、 -この層に何も頼っていないから。逆にこの層が要る CRD(Gateway API・cert-manager)も machine config が入れる。 `InfisicalSecret` の CRD だけは `apps/infisical-operator/` が入れるので、まっさらなクラスタでは `bootstrap` の同期が 最初の何回か落ちて retry で戻る。 @@ -38,14 +35,13 @@ ConfigMap に出ているので、読むだけの権限で比べられる)。** 平文の Secret はアプリのリポジトリにはコミットしない。値は **Infisical**(https://il.doany.io、このクラスタでセルフホスト)が持ち、 **Infisical 純正 operator**(`apps/infisical-operator/`)が各リポジトリの `InfisicalSecret` を見て Infisical から値を取り、普通の `Secret` を作る。 -`secrets.infisical.com/auto-reload` 注釈のある Deployment は Secret 更新時に自動で入れ替わる。 認証は Kubernetes 方式(TokenReview・長命の資格情報なし)。 - `infisical/` … Infisical 本体(chart + Postgres/Redis)。公開経路は [`apps/infisical/httproute.yaml`](../apps/infisical/httproute.yaml)。 `secrets.sops.yaml` の `ENCRYPTION_KEY` が DB の暗号鍵で、これを失うと Infisical の中身が全部読めなくなる (クラスタで一番失ってはいけない値)。Postgres の PVC は既定の `local-path` で、`pg_dump` を k8up が日次で取っている。 -- **operator と push-bridge はここには無い。** ArgoCD の Application に移した(`apps/infisical-operator/`、 - `apps/infisical-push-bridge/`)。ArgoCD 自身は operator に依存しないので、bootstrap に置く理由がない。 +- **operator と push-bridge はここには無い**(`apps/infisical-operator/`、`apps/infisical-push-bridge/`)。 + ArgoCD 自身は operator に依存しないので、bootstrap に置く理由がない。 認証は Infisical の Kubernetes auth: operator が SA `infisical-auth` のトークンで Infisical にログインし、 Infisical がそのトークンで TokenReview を呼んで本物か確かめる(なので SA に `system:auth-delegator` を 付けてある)。SA と RBAC は `apps/infisical-operator/rbac.yaml`。 @@ -59,7 +55,7 @@ ConfigMap に出ているので、読むだけの権限で比べられる)。** CA は [apiserver/ca.crt](apiserver/ca.crt)(`talos/secrets.yaml` のクラスタ CA。作り直したら替える)。 kubeconfig も age 鍵も GitHub に置いていない。 -**同じ認証設定と RBAC が人の入口でもある(2026-09-09)。** Entra を 2 つめの issuer として足し、 +**同じ認証設定と RBAC が人の入口でもある。** Entra を 2 つめの issuer として足し、 Headlamp が利用者本人の id_token を API サーバへ転送する。アプリロール `admin` を持つ人は `entra:admin` で cluster-admin、それ以外は `entra:viewer` で読み取り専用(組み込みの `view` + CRD とノードぶんの `readonly-extra`)。**振り分けは CEL で、割り当ては Entra 側で完結する。** @@ -70,9 +66,7 @@ machine config に載っているので、変えたら `render.sh` → `apply-co ### 公開経路(HTTPRoute)もここにある -`argocd` と `auth` の HTTPRoute と `gateway/redirect-https.yaml` はここ。アプリ側の経路はアプリと同じ場所 -(`apps//httproute.yaml`、または各アプリのリポジトリの `deploy/httproute.yaml`)。 -ArgoCD が壊れても当たっている経路は消えないので、直すのは Headlamp か `tools/t.ps1 --admin` の kubectl +`argocd` と `auth` の HTTPRoute と `gateway/redirect-https.yaml` はここ ([docs/decisions.md](../docs/decisions.md)「公開経路(HTTPRoute)をどこに置くか」)。 ### Infisical の中の構成 @@ -95,15 +89,14 @@ argocd.argoproj.io/sync-options: Prune=false,SkipDryRunOnMissingResource=true **`Prune=false`** ── operator は CR の annotation を、作った `Secret` にもコピーする。 ArgoCD の tracking-id までコピーされると、ArgoCD がその `Secret` を「git に無い管理対象」と -見なして sync のたびに prune する(復元リハーサル 2026-09-06 で発覚)。 +見なして sync のたびに prune する。 `Prune=false` も一緒にコピーさせて、prune の対象から外す。 **`SkipDryRunOnMissingResource=true`** ── **まっさらなクラスタでは CRD がまだ無く、`apps/` の同期が丸ごと落ちる** -(CRD を入れる `apps/infisical-operator/` が同じ同期の中にいる。2026-09-08 のドリル 5 回目)。 +(CRD を入れる `apps/infisical-operator/` が同じ同期の中にいる)。 仕組みは k8up の `Schedule` と同じで、説明は [apps/k8up/README.md](../apps/k8up/README.md)「CRD がまだ無いクラスタで apps を止めないこと」。 -`bootstrap/` の `InfisicalSecret`(`argocd/oidc-secret.yaml`・`auth/secrets.yaml`・`infisical/smtp-secret.yaml`)にも付けてある -(2026-10-17 に ArgoCD の `bootstrap` が見るようになった)。 +`bootstrap/` の `InfisicalSecret`(`argocd/oidc-secret.yaml`・`auth/secrets.yaml`・`infisical/smtp-secret.yaml`)にも付けてある。 ### アプリ側の書き方 diff --git a/docs/cloudflare-tokens.md b/docs/cloudflare-tokens.md index 99efa9f..2aa13a2 100644 --- a/docs/cloudflare-tokens.md +++ b/docs/cloudflare-tokens.md @@ -2,9 +2,9 @@ アプリが使う Cloudflare のトークンは **Pulumi で作らず、画面(か `cf`)で作って手で Infisical に入れる**。 Pulumi で作ると値を Infisical に書く鍵が CI に要る。その鍵は prod 全体の読み書き・削除ができる Member になる -(Infisical の最小権限のカスタムロール・追加権限は Enterprise 限定)。漏れたときの範囲に見合わない(2026-10-07 に決めた)。 +(Infisical の最小権限のカスタムロール・追加権限は Enterprise 限定)。漏れたときの範囲に見合わない。 -## 一覧(2026-10-07) +## 一覧 | トークン | 種類 | 権限 | 置き場所 | 使うもの | | --- | --- | --- | --- | --- | diff --git a/docs/decisions.md b/docs/decisions.md index 21c57ca..f8afcea 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -12,18 +12,15 @@ (`pvc-__`)、Infisical のデータと `ENCRYPTION_KEY`、ArgoCD + ApplicationSet + 各 repo の `deploy/argocd.yaml`、 **復元手順は公開で秘密を含まない**という性質。 -## Z440 へは Talos のまま移す(2026-10-17) +## Z440 へは Talos のまま移す -一番危ない OS の入れ替え(Talos への移行)は、**iLO で画面まで見える DL360** で済ませた。Z440 の遠隔管理は -AMT 9.x(電源と SOL だけ)で、単体の GPU が無いと POST もしない。Talos なら箱の違いは schematic と NIC の名前に閉じる。 -計画と箱の違いは [z440-migration.md](z440-migration.md)。 +OS の入れ替えは **iLO で画面まで見える DL360** で済ませた。Z440 の遠隔管理は AMT 9.x(電源と SOL だけ)なので、 +箱の違いは schematic と NIC の名前に閉じ込める([z440-migration.md](z440-migration.md))。 -## bond0 の MAC を固定する(2026-10-17) +## bond0 の MAC を固定する -ルーターの IPv6 ホスト公開はノードを **bond0 の MAC(`e6:2c:1b:1a:a0:39`)で指している** -([router.md](router.md)「IPv6 はノードだけを公開する」)。固定しないと bond は先頭のメンバーの MAC を引き継ぎ、 -再起動で変わってエントリから外れたことがある。[talos/patches/cluster.yaml](../talos/patches/cluster.yaml) の `BondConfig` の -`hardwareAddr` に書いたので、NIC の交換でも Z440 への移設でも変わらない。 +ルーターの IPv6 ホスト公開が **bond0 の MAC で指している**ので、`BondConfig` の `hardwareAddr` で固定した +([router.md](router.md)「IPv6 はノードだけを公開する」)。 ## ツール選定(比較の結論) @@ -41,7 +38,7 @@ AMT 9.x(電源と SOL だけ)で、単体の GPU が無いと POST もしない **結論: restic(k8up)。** k8up の中身は素の restic なので、k8up が気に入らなければ CronJob + restic イメージに落とせるし、 k8up の取ったスナップショットも手元の restic CLI で読める(Infisical はそうやって戻す)。 -リモートは Cloudflare R2 のバケット `doany-restic`(APAC、Standard。2026-09-06)。API トークンは +リモートは Cloudflare R2 のバケット `doany-restic`(APAC、Standard)。API トークンは Object Read & Write をこのバケットだけに絞った Account API token。下り(egress)が無料なので復元で費用が跳ねない。 ### 手で持つもの(根っこ) @@ -64,7 +61,7 @@ TTL も付いている。使い捨てにするほうが安全(../talos/README.md R2 の鍵を GitHub の秘密(gist・Actions)に置かないのは、GitHub のトークンがあちこちに散っているため。 GitHub 側が丸ごと漏れても、バックアップ(を消せる鍵)だけは無事な状態を残す。 -### そのパスフレーズをどこに置くか(2026-09-08 決定) +### そのパスフレーズをどこに置くか **公開 repo に暗号文が載っている以上、オフラインで無限に総当たりされる。強度がすべて**なので diceware 6〜7 語相当を使う。**同期ストレージ(OneDrive など)を正本にしない** ── @@ -89,7 +86,7 @@ diceware 6〜7 語相当を使う。**同期ストレージ(OneDrive など)を (RDBMS は `pg_dump`、SQLite は `bun:sqlite` の `serialize()`、ファイルの PVC は注釈)。**どれが何で取れているかの一覧は [../apps/k8up/README.md](../apps/k8up/README.md)** ── ここには写さない(二重に持つと片方が腐る)。 -## Talos の起動順序をどう組むか(2026-09-07) +## Talos の起動順序をどう組むか Talos には helm-controller が無く、inlineManifests は Helm を実行できないので、手元の `render.sh` が `helm template` して載せる。 **ArgoCD は Infisical 無しで起動する。** `$argocd-oidc:client-secret` のような参照は Secret が無いと警告して文字列のまま返す @@ -106,7 +103,7 @@ Talos には helm-controller が無く、inlineManifests は Helm を実行で - **CRD だけ URL で渡すのは大きさの都合。** 埋めると machine config が 341 KB → 4 MB を超える。 `KubeExternalManifestConfig` は Talos がそのために用意している入口([talos/render.sh](../talos/render.sh)) -- **平文の残りは ArgoCD(2026-10-17 まで GitHub Actions の `bootstrap-apply.yml`)。** 鶏と卵にならない ── +- **平文の残りは ArgoCD。** 鶏と卵にならない ── ArgoCD が動くのに要るもの(本体・repo の資格情報・入口の `apps`)は全部 machine config にあり、この層に頼っていない。 この層が要る CRD も machine config が入れる(`InfisicalSecret` の CRD だけ `apps/` から来るので、最初の何回かは retry)。 - **SOPS の Secret を machine config に入れる。** 復号鍵はクラスタにも CI にも渡さないので、ArgoCD からは当てられない。 @@ -114,7 +111,7 @@ Talos には helm-controller が無く、inlineManifests は Helm を実行で - **Helm を実行する場所はクラスタの外(手元の生成時)だけ。** Cilium は CNI なので inlineManifests 以外に置きようがなく、 ArgoCD も同じ扱いにすればクラスタ内に Helm コントローラを持たなくて済む。版の追従は Renovate -## machine config と Cilium の chart をどう連動させるか(2026-09-08 決定) +## machine config と Cilium の chart をどう連動させるか **machine config を「派生物」にする。** `helm template` の出力を `KubeInlineManifestConfig` に包んで `--config-patch` として渡す ([talos/render.sh](../talos/render.sh))ので、Cilium の値がリポジトリに二度書かれない。Renovate は @@ -125,36 +122,33 @@ Talos には helm-controller が無く、inlineManifests は Helm を実行で 流し忘れは [cilium-drift.yml](../.github/workflows/cilium-drift.yml) が週次で live と突き合わせる。 bootstrap のあとに `helm install` する案は、再構築のたびに「CNI が無いので何も動かない」状態での手作業が増えるので採らない。 -**描いた結果はコミットしない(2026-10-17 に再検討)。** 描き出しを git に置いて Renovate の `postUpgradeTasks` に描き直させれば -当てるのは「重ねるだけ」になるが、採らない: -- **infisical の描き出しは秘密を含む。** chart が Redis の接続文字列(`REDIS_URL`)をパスワードごと Deployment の env に書き、 - 外から渡す口が無い(1.11.0 のテンプレートで確認)。ここだけは手元で描くしかなく、描く道具は結局残る +**描いた結果はコミットしない。** 描き出しを git に置いて Renovate に描き直させる案は採らない: +- **infisical の描き出しは秘密を含む**(下の「infisical だけは ArgoCD に移せない」)。ここだけは手元で描くしかなく、描く道具は結局残る - 描き出しは machine config で 980 行・310 KB(1 行が長い)。版を上げるたびにその差分が積もる - 代わりに **描くたびに変わる値を潰した**(Hubble の証明書・infisical の `postgres-password`・`updatedAt`)。 同じ入力なら生成物はバイト単位で同じなので、`apply-config --dry-run` の差分がそのまま「何が変わるか」になる -**起動に要らないものは ArgoCD に置く。** metrics-server は 2026-10-17 に `apps/metrics-server/` へ移した -(版上げが `upgrade-k8s` 無しで済む)。cert-manager は移さない ── CRD を machine config から外すと Talos の prune が CRD を消しうる(消えれば +**起動に要らないものは ArgoCD に置く**(metrics-server は `apps/metrics-server/`。版上げが `upgrade-k8s` 無しで済む)。cert-manager は移さない ── CRD を machine config から外すと Talos の prune が CRD を消しうる(消えれば 全 namespace の Certificate が道連れ)。 -## ルーティングの選定(2026-09-06) +## ルーティングの選定 -**Cilium(CNI + Gateway API)に寄せる。** `Ingress` API は凍結済みで ingress-nginx は 2026-03 に retire。 +**Cilium(CNI + Gateway API)に寄せる。** `Ingress` API は凍結済みで ingress-nginx は retire。 **Talos で後から替えるのが高いのは CNI だけ**なので先に決め、Ingress・LB・証明書は Gateway API に寄せて後から差し替えられるようにした。 -| | 選択 | 置き換わるもの | -| --- | --- | --- | -| CNI | Cilium | flannel | -| kube-proxy | Cilium の kubeProxyReplacement | kube-proxy | -| LoadBalancer | 使わない(hostPort と Gateway の hostNetwork。下記) | ServiceLB | -| Ingress | Cilium Gateway(Gateway API) | Traefik | -| 証明書 | cert-manager(Cloudflare DNS-01) | Traefik 内蔵 ACME | -| 認証 | oauth2-proxy を前段のプロキシに(下記) | — | +| | 選択 | +| --- | --- | +| CNI | Cilium | +| kube-proxy | Cilium の kubeProxyReplacement | +| LoadBalancer | 使わない(hostPort と Gateway の hostNetwork。下記) | +| Ingress | Cilium Gateway(Gateway API) | +| 証明書 | cert-manager(Cloudflare DNS-01) | +| 認証 | oauth2-proxy を前段のプロキシに(下記) | - **Cilium 1.20 は Gateway API v1.6.1 の CRD を要求する。** 古いと GatewayClass が `Waiting for controller` のまま止まる - **`terminationGracePeriodSeconds` が長い Pod**(denpa と tuner-agent、6 時間)は Recreate と組むと入れ替わらない。`--grace-period=0 --force` で消す -### LoadBalancer をどう置き換えるか(2026-09-06 決定・実施済み) +### LoadBalancer をどう置き換えるか **hostPort を選んだ。** ルータの DMZ 転送先と AdGuard の split-horizon が**ノード自身の IP**(`10.0.0.2` / `10.10.0.4` / `240f:6d:842b:1::2`)に固定されていて、LB-IPAM の仮想 IP だとクラスタ外の変更が要り、IPv6 のプレフィックスは RA 由来で変わりうる。 @@ -178,11 +172,11 @@ hostPort ならノードの全アドレスで受けられ、送信元 IP も保 - **3proxy**(TLS 終端 + 素の TCP): Cilium は `mode: Terminate` のリスナーに TLSRoute しか許さず、同じ 443 に Terminate と Passthrough を 同居させると ProtocolConflict で 443 ごと落ちる。nginx(stream)のサイドカーが **hostPort 3129** で終端して `127.0.0.1:3128` に渡す -- **AdGuard の DoH**: AdGuard は `/dns-query` を HTTPS の口(443)でしか出さない(平文の 80 は 404。切り替えの翌朝、ブラウザの名前解決だけが死んだ)。 +- **AdGuard の DoH**: AdGuard は `/dns-query` を HTTPS の口(443)でしか出さない(平文の 80 は 404)。 `BackendTLSPolicy` は Cilium 1.20.1 が Accepted にするだけで実装していない。nginx のサイドカーが `/dns-query` だけを `https://127.0.0.1:443` へ回す。Cilium が `BackendTLSPolicy` を実装したら両方まとめて消せる -### クライアント IP(2026-09-07 確認、`Cluster` のままでよい) +### クライアント IP(`Cluster` のままでよい) Gateway の Service は `externalTrafficPolicy: Cluster`。**単一ノードでは backend が必ず同じノードに居るので SNAT されない** (denpa は公開側から 401・LAN から 200、AdGuard の DoH のログに端末の IPv6 が残る)。 @@ -198,17 +192,15 @@ Entra のトークンは大きく(セッション実測 4293 バイト)、Cookie ### sticky cookie は Cilium 1.21 待ち -Traefik の sticky cookie に代わるものが無い(使っていたのは tamasagashi 1 本。更新中に HTML と CSS/JS が別の Pod から返る 404 を防ぐため)。 +Gateway に sticky cookie が無い(要るのは tamasagashi 1 本。更新中に HTML と CSS/JS が別の Pod から返る 404 を防ぐため)。 Gateway API の session persistence は experimental チャネル限定、Cilium は 1.21 から(cilium/cilium#48029)、 `sessionAffinity: ClientIP` は Gateway 経路に効かない。いまは replicas 1 で穴を受け入れている。 **1.21 に上げるときに Gateway API CRD を experimental に替え(`talos/render.sh` の URL)、tamasagashi の HTTPRoute に `sessionPersistence` を足す。** それまでにアプリを複数 replica にするなら、セッション固定が無いことを先に確かめる。 -## chart を配るときと、chart で入れるとき(2026-09-07) +## chart を配るときと、chart で入れるとき -**`HelmChart` CRD(helm-controller)はやめた。** ArgoCD と二重管理になり(差分も同期状態も prune も見えない)、Talos に無い。 -argocd と infisical も `bootstrap//` の version.yaml・values.yaml から `render.sh` が描く。**Helm chart を配る(OCI で push する)のは続ける**(denpa と yosegaki)。 -入れ方は利用者に選ばせる(`helm install` でも `Application` でも同じものが入るのが正しい)。このクラスタは ArgoCD の `Application` に統一した。 +**Helm chart を配る(OCI で push する)**のは denpa と yosegaki。入れ方は利用者に選ばせる(`helm install` でも `Application` でも同じものが入るのが正しい)。このクラスタは ArgoCD の `Application` に統一した。 配る chart でやらないこと(踏んだもの。自分の chart はどれも該当しない): @@ -220,7 +212,7 @@ argocd と infisical も `bootstrap//` の version.yaml・values.yaml か **別のリリース名で入れ直すと別物として作り直しになる**(名前を切り詰める chart がある)。入れ方を替えるときは `helm template` と live を先に突き合わせる。 -### infisical だけは ArgoCD に移せない(2026-09-08、chart を読んで確定) +### infisical だけは ArgoCD に移せない **理由は鶏卵ではなく chart の作り。** `infisical-standalone` 1.10.0 は `DB_CONNECTION_URI` と `REDIS_URL` を `.Values.*.auth.password` から **Deployment の平文 env に焼き込む**(`templates/_helpers.tpl`)ので、平文の Application に値を書けない。 @@ -229,56 +221,42 @@ argocd と infisical も `bootstrap//` の version.yaml・values.yaml か **なので `bootstrap/` に残し、machine config の `inlineManifests` に載せる。** `render.sh` が SOPS 済みの値を復号して `helm template` に渡すので、 平文は machine config の中にしか出ない。 -## ghcr の pull 認証 - -**2026-09-15 から ghcr.io の資格情報は要らない** ── 非公開のリポジトリを Forgejo に移し(apps/forgejo/README.md)、 -ghcr.io に残るイメージは全部公開。**ノード単位で持つのは fj.doany.io の資格情報だけ**(`talos/registries.yaml`、[talos/README.md](../talos/README.md))。 - -## PT3 チューナー(Talos で動かすための算段) +## PT3 チューナー -実機には Earthsoft PT3(`earth_pt3`、Altera 1172:4c15)が刺さっていて、Talos のカーネルは `CONFIG_DVB_PT3` を -有効にしておらず、公式 extension にも無かった。denpa の tuner-agent はこの箱の `/dev/dvb` と B-CAS リーダーに依存している。 - -**結論(2026-10-13): 上流に入った。** [siderolabs/pkgs#1682](https://github.com/siderolabs/pkgs/pull/1682) -(`CONFIG_DVB_PT3=m`)が v1.15.0-alpha.1 に入り、`siderolabs/dvb-pt3` が Image Factory の公式 extension に出た。 -本番の schematic を `96371c8f…`(dvb-pt3 + intel-ucode + IOMMU の引数)に作り直し、Talos を v1.15.0-alpha.1 に上げた -(`talos/versions.yaml`)。extension は `earth_pt3` を blacklist して配るので、`talos/patches/main.yaml` の -`machine.kernel.modules` で読ませる。alpha をノード 1 台の本番に乗せることになるが、正式版を待つより PT3 を先に取った。 +denpa の tuner-agent は Earthsoft PT3(`earth_pt3`)の `/dev/dvb` と B-CAS リーダーに依存している。 +**公式 extension `siderolabs/dvb-pt3` を使う**([siderolabs/pkgs#1682](https://github.com/siderolabs/pkgs/pull/1682))。 +v1.15.0-alpha.1 からしか無いので、**正式版を待つより PT3 を先に取り、本番を alpha に乗せている**。 +extension は `earth_pt3` を blacklist して配るので、`talos/patches/main.yaml` の `machine.kernel.modules` で読ませる。 **駄目だったときの保険: KubeVirt の VM にパススルー。** PT3(05:00.0)は IOMMU グループ 36 に単独、B-CAS は USB(GemPC Twin 08e6:3437)。 `intel_iommu=on` は schematic に残してあるが、vfio は `talos/patches/main.yaml` から外した(PT3 をホストのドライバと取り合う)ので、戻るなら足し直す。 **自前 extension を `imager` で焼く案は採らない**(アップグレードのたびに自前ビルドになる)。 -## 依存の更新をどこまで自動で入れるか(2026-09-07) +## 依存の更新をどこまで自動で入れるか 共有プリセット `5ym/repo-config` で、**パッチとマイナーは 1 つの PR にまとめて自動マージ、メジャーと Helm chart は人が見る** -(postgres 17 → 18 が nginx のパッチと同じ PR で自動マージされかけた danything/gitops#7、chart のパッチ更新で中身が入れ替わった件から)。 +(chart はパッチ更新でも中身が入れ替わることがある)。 PostgreSQL は 17 の線に固定(メジャーでデータディレクトリの互換が切れ、dump / restore が要る)、argo-cd と infisical-standalone の chart は自動マージしない。 理由はそれぞれ [../renovate.json](../renovate.json) の `description`。Renovate 自体は [apps/renovate/](../apps/renovate/) の CronJob で動く。 -## PVC を守る仕掛けをやめた(2026-09-07) +## PVC を守る仕掛けをやめた **「git で消したものは消える」を優先する**、という判断(本人)。PVC の `Prune=false,Delete=false` は git から消しても PVC が残り、 -StorageClass `local-path-retain`(`reclaimPolicy: Retain`)は追われない Released の PV を作っていた(35 日と 44 日放置が 3 本)。 +`reclaimPolicy: Retain` は追われない Released の PV を作っていた。 -**代わりの後ろ盾はバックアップ。** 誤って消したときの最大損失は 24 時間ぶん。**この判断は復元リハーサルが通ってから**行った -(2026-10-17 には実際にこれで全部戻した)。 +**代わりの後ろ盾はバックアップ。** 誤って消したときの最大損失は 24 時間ぶん。 **承知しておくこと**: `Delete=false` も外したので、**Argo CD の Application を消すと PVC も消える**。 マニフェストを消すのは git のレビューを通るが、Application の削除は UI のボタン 1 つで済む。 **`storageClassName` はバインド済み PVC では変更も削除もできない。** 変えるときは PVC を止め、PV を `Retain` にして古い PVC を消し、 -PV の `claimRef` を外して書き換え、新しい PVC を `volumeName` で同じ PV に結ぶ(2026-10-05 に 28 本をこれで付け替えた)。 +PV の `claimRef` を外して書き換え、新しい PVC を `volumeName` で同じ PV に結ぶ。 ## バックアップに何を含めるか -**有料でも取る(2026-09-08)。** R2 の無料枠(10 GB-month)に収めることを目的にすると録画を捨てることになる。 +**有料でも取る。** R2 の無料枠(10 GB-month)に収めることを目的にすると録画を捨てることになる。 容量が問題になるのは桁が変わったとき(数百 GB)。課金は期間中の日次ピークの平均で、操作の回数は桁違いに余っている。 -いまの姿(2026-10-17): - -- **restic の実データは 5.60 GiB**(圧縮前 8.52 GiB)。前のホストのスクリプト(`host=main`)が PV の置き場を丸ごと持っていて、 - k8up の世代を減らしても同じ blob を掴んで解放されなかったが、2026-10-17 に forget + prune した - 保持は k8up の Schedule で `keepDaily 7 / keepWeekly 4 / keepMonthly 6`(遡れるのは約半年)。etcd は日次で `keepDaily 7 / keepWeekly 4` ([../apps/k8up/etcd-snapshot.yaml](../apps/k8up/etcd-snapshot.yaml)) - 戻せるかは毎月 1 日に [../apps/k8up/restore-check.yaml](../apps/k8up/restore-check.yaml) が最新を取り出して確かめる @@ -289,12 +267,10 @@ PV の `claimRef` を外して書き換え、新しい PVC を `volumeName` で | 生 TS の作業領域(`denpa-raw`) | **取らない。** エンコードが終われば消え、数時間で入れ替わる。14 GB まで育つ | | AdGuard のクエリログ | **保持を 7 日に。** 純粋な計測データで、復元時に無くても困らない | -## VPN を wg-easy から NetBird にした(2026-09-07) +## VPN を wg-easy から NetBird にした -要るのは **プライバシー(全部を自宅経由にする)・DNS(AdGuard)・iLO に入る**の 3 つで、スマホを足すときの QR はよく使う。 -wg-easy は自前の認証を切れず SSO の後ろでも二重ログインが残り([wg-easy#1923](https://github.com/wg-easy/wg-easy/issues/1923))、 -Headscale は FAQ が「headscale を動かすマシンをサブネットルータにするな」と書いていてノード 1 台では避けられない。 -**NetBird は routing peer が独立した概念で、1 台構成でも問題が起きない。** +要るのは **プライバシー(全部を自宅経由にする)・DNS(AdGuard)・iLO に入る**の 3 つ。 +**NetBird は routing peer が独立した概念で、1 台構成でも問題が起きない**(Headscale は管理サーバーとサブネットルータの同居を避けよと書いている)。 **公式 Helm chart は使わない**(更新が止まっている。[netbirdio/helms#39](https://github.com/netbirdio/helms/issues/39))。 v0.65.0 から management/signal/relay が 1 つの combined コンテナなので、素の Deployment で足りる(`apps/netbird/`)。 @@ -306,28 +282,22 @@ v0.65.0 から management/signal/relay が 1 つの combined コンテナなの - **外部 IdP(Entra)は設定ファイルに書けない**(0.62 以降はストアに入るデータ)。ダッシュボードか `POST /api/identity-providers` で足す。 リダイレクト URI は `https://nb.doany.io/oauth2/callback` 固定 -## 公開経路(HTTPRoute)をどこに置くか(2026-09-07) +## 公開経路(HTTPRoute)をどこに置くか -`apps/gateway-routes/` にまとめていたのをやめ、**それぞれが公開しているものと同じ場所**に置いた +**それぞれが公開しているものと同じ場所**に置く (このリポジトリの `apps//httproute.yaml`、各アプリのリポジトリの `deploy/httproute.yaml`、`bootstrap/` の argocd / auth / redirect-https)。 -ArgoCD は `recurse` で拾い tracking-id 注釈で追跡するので、場所は挙動に影響しない。 -**`bootstrap/` に移したものが本題。** `argocd` と `auth` の実体はこの層にある。 -当時は「ArgoCD が自分の公開経路を握ると、ArgoCD が壊れたときに直せない」として GitHub Actions に当てさせていたが、 -2026-10-17 に ArgoCD(Application `bootstrap`)へ移した。ArgoCD が壊れても当たっている経路は消えず、 -直すのは Headlamp か `tools/t.ps1 --admin` の kubectl で足りる。GitHub Actions の書き込みの経路(ワークフロー・RBAC・CEL)が丸ごと消えた。 +`bootstrap/` の経路も ArgoCD(Application `bootstrap`)が当てる。ArgoCD が壊れても当たっている経路は消えず、 +直すのは Headlamp か `tools/t.ps1 --admin` の kubectl で足りる。 外部露出の一覧は `kubectl get httproute,grpcroute -A` が正確で、git を読むより早い(一覧の一枚は作らない)。 **落とさずに動かす**: ArgoCD は「git から消えた + 自分が追跡している」ものを prune する。別リポジトリへは移送先を**先に**マージして tracking-id が書き換わってから元を消す。`bootstrap/` へは、先に live に `Prune=false` を効かせてから移し、tracking-id 注釈を剥がす。 -## DNS:直接つなぐアプリと Cloudflare を通すアプリを分ける(2026-10-06) +## DNS:直接つなぐアプリと Cloudflare を通すアプリを分ける **社内用や通信量が多いアプリは直接つなぐ。SaaS のように外へ出すアプリは Cloudflare のプロキシを通す**(本人)。 - -- 直接つなぐもの:`*.doany.io` のワイルドカード(proxied ではない CNAME → `doany.io`)で引ける。明示のレコードは持たない -- Cloudflare を通すもの:アプリのリポジトリの HTTPRoute に `external-dns.kubernetes.io/cloudflare-proxied: "true"` を付け、 - **ExternalDNS**(`apps/external-dns/`)が proxied の CNAME を作る +やり方は [apps/external-dns/README.md](../apps/external-dns/README.md)。 **DNS もアプリのリポジトリで持つ(疎結合)。** 中央の IaC(Pulumi・DNSControl・OctoDNS)に一覧を置くと、アプリを足すたびに 2 か所を直す。 ExternalDNS は HTTPRoute の注釈だけで決まり、自分が作ったレコード(所有の印の TXT 付き)にしか触れないので、メールや DDNS の apex と同居できる。 diff --git a/docs/entra.md b/docs/entra.md index 03bfc3f..f7cf458 100644 --- a/docs/entra.md +++ b/docs/entra.md @@ -2,12 +2,12 @@ ## いまの決まり -**認可はアプリロールで決める。テナントに認可用のグループは無い**(グループクレームは 2026-09-07 に消し、`admins` グループも削除した)。 +**認可はアプリロールで決める。テナントに認可用のグループは無い**。 共用のアプリ登録 `Main`(client id `b0fa498f-7e6a-4fe1-a1c6-16fbbb6f397e`)にロール `Admins`(値 `admin`)があり、 各アプリはトークンの `roles` に `admin` があれば管理者にする。ロールとリダイレクト URI は Pulumi が持つ([../pulumi/entra.ts](../pulumi/entra.ts))。 - **ロールはユーザーに直接割り当てる。** テナントに Entra ID P1/P2 が無く、グループ単位の割り当てができない。管理者が増えたらその人にも割り当てる -- **`appRoleAssignmentRequired` は `false` のまま**(2026-10-07、本人)。`はい` にすると、ロールの無い人が `Main` の後ろのアプリ全部から締め出され、 +- **`appRoleAssignmentRequired` は `false` のまま**(本人)。`はい` にすると、ロールの無い人が `Main` の後ろのアプリ全部から締め出され、 人を足すたびに割り当てが要る。誰が入れるかはテナントに居るかどうか、何ができるかは各アプリ側で絞る - ロールにした理由: 設定が読める(`g, admin, role:admin`)、ロール名は他の IdP にも移せる、トークンがテナントのグループの数に左右されない @@ -19,7 +19,7 @@ ## ゲストを足したときに使えるもの -方針は「**使うものは全部使える、管理は `admin` のロールを持つ人(info@)だけ**」(2026-10-07 に本人が決めた)。 +方針は「**使うものは全部使える、管理は `admin` のロールを持つ人(info@)だけ**」(本人)。 ゲストを Entra に招くだけで、割り当てなどは要らない。 | 種類 | ゲスト | 決めているところ | @@ -34,10 +34,10 @@ Forgejo の members から外したいときは [../apps/forgejo/README.md](../a ## oauth2-proxy のセッションは Cookie だけ(redis なし) -セッションが 4293 バイトあったのはグループではなく **ID・アクセス・リフレッシュの 3 つのトークンそのもの**だった -(グループは 2 つで 100 バイト強)。**`OAUTH2_PROXY_SESSION_COOKIE_MINIMAL=true`** でそれを落とし、Email と User だけを Cookie に持つ。 +セッションを大きくしていたのは **ID・アクセス・リフレッシュの 3 つのトークンそのもの**(合わせて 4 KB 超)。 +**`OAUTH2_PROXY_SESSION_COOKIE_MINIMAL=true`** でそれを落とし、Email と User だけを Cookie に持つ。 `--pass-authorization-header` / `--set-authorization-header` / `--pass-access-token` / `--cookie-refresh` とは併用できない(使っていない)。 -redis は 2026-09-07 に消した。切り戻すなら `SESSION_STORE_TYPE=redis` と `REDIS_CONNECTION_URL` を戻して入れ直す(セッションは消える)。 +redis に戻すなら `SESSION_STORE_TYPE=redis` と `REDIS_CONNECTION_URL` を足して入れ直す(セッションは消える)。 効いたかは oauth2-proxy のログで見る。`groups:[admin]` ならロールで通り、`refresh_token:false` ならトークンが落ちている。 @@ -47,5 +47,3 @@ kubectl logs -n auth deploy/auth --since=10m | grep AuthSuccess **`OIDC_GROUPS_CLAIM` を generic な `oidc` プロバイダで使うと `groups` スコープを勝手に要求し、Entra に AADSTS650053 で断られる** (`bootstrap/auth/deployment.yaml` のコメント)。いまは `entra-id` プロバイダで `OAUTH2_PROXY_SCOPE` も明示している。 - -wg-easy 用の別のアプリ登録 `wg-easy` が残っていれば消してよい(VPN は 2026-09-07 に NetBird へ移した)。 diff --git a/docs/router.md b/docs/router.md index fd1f3c4..76278fa 100644 --- a/docs/router.md +++ b/docs/router.md @@ -1,7 +1,6 @@ # 家のルーター -ルーターには API が無いので、設定はここに書いて持つ(IaC にはできない)。これまで decisions.md・apps/matrix/README.md・ -docs/talos.md に散らばっていたものを 1 つにまとめた(2026-10-07)。**ルーターの設定を変えたら、ここも直す。** +ルーターには API が無いので、設定はここに書いて持つ(IaC にはできない)。**ルーターの設定を変えたら、ここも直す。** 値は 2026-10-07 に管理画面(クイック設定Web)とノードで読んだもの。 @@ -9,12 +8,12 @@ docs/talos.md に散らばっていたものを 1 つにまとめた(2026-10-07) | 項目 | 値 | | --- | --- | -| 機種 | au ひかりのホームゲートウェイ **BL1500HM**(KDDI。管理画面の「クイック設定Web」のページから。2026-10-07) | +| 機種 | au ひかりのホームゲートウェイ **BL1500HM**(KDDI) | | 管理画面 | `http://10.0.0.1/`(クイック設定Web。ログインが要る) | | 回線 | au ひかり(接続はホームゲートウェイが持つ。IPv6 はネイティブで RA が来る) | | 外向きの IPv4 | 固定ではない。cloudflare-ddns が `doany.io` と `nb.origin.doany.io` を追従させる(apps/cloudflare-ddns/) | | IPv6 | プレフィックス `240f:6d:842b:1::/64`(RA で配られる。**変わりうる**ので、どこにも固定で書かない) | -| IPv6 の受信 | 「IPv6ホスト公開」の既定は**「遮断」**で、**ノードだけをエントリで公開**(MAC `e6:2c:1b:1a:a0:39`、IPv6 すべて、ポート any)。2026-10-07 に「公開」(全端末)から変えた(下の「IPv6 はノードだけを公開する」)。IPv6 パケットフィルターは有効で、WAN 側の規則は 0 件 | +| IPv6 の受信 | 「IPv6ホスト公開」の既定は**「遮断」**で、**ノードだけをエントリで公開**(MAC `e6:2c:1b:1a:a0:39`、IPv6 すべて、ポート any。下の「IPv6 はノードだけを公開する」)。IPv6 パケットフィルターは有効で、WAN 側の規則は 0 件 | ## LAN @@ -48,17 +47,16 @@ Talos の API(50000)は LAN からしか届かない(recovery/README.md)。 ## 53 番は外にも開けている(意図して) AdGuard Home は問い合わせ元を絞っておらず(apps/adguardhome/deployment.yaml の `CONFIG` の `allowed_clients: []`)、DMZ で 53 番も外に出ている。 -**意図してそうしている**(2026-10-07 に確認)。オープンリゾルバとして DNS の増幅攻撃の踏み台に使われうることは承知の上。 +**意図してそうしている**。オープンリゾルバとして DNS の増幅攻撃の踏み台に使われうることは承知の上。 閉じるときは、ルーターで 53 番の受信を止めるか、`allowed_clients` に家の LAN・Pod・NetBird の範囲だけを書く。 ## IPv6 はノードだけを公開する -「IPv6ホスト公開設定」の既定を「遮断」にし、ノード `main` だけをエントリで公開している(2026-10-07。それまでは既定が「公開」で、 -LAN の全端末に外から IPv6 で直接届いていた)。 +「IPv6ホスト公開設定」の既定を「遮断」にし、ノード `main` だけをエントリで公開している(既定の「公開」だと LAN の全端末に外から IPv6 で直接届く)。 - **エントリは MAC アドレスで指す**(bond0 の `e6:2c:1b:1a:a0:39`。`talosctl -n 10.0.0.2 get links bond0` で見る。ルーターの「接続端末 情報画面」にも同じ値が出る)。IPv6 アドレスで指すと、RA のプレフィックスが変わったときに外れる -- **この MAC はノードの設定で固定している**(`talos/patches/cluster.yaml` の `BondConfig` の `hardwareAddr`)。固定していなかった頃は - 再起動で `3a:43:95:a9:3e:b1` に変わってエントリが外れた。NIC を替えても Z440 に移しても、この値は変わらない +- **この MAC はノードの設定で固定している**(`talos/patches/cluster.yaml` の `BondConfig` の `hardwareAddr`)。固定しないと bond は先頭のメンバーの MAC を引き継ぎ、 + 再起動で変わってエントリが外れる。NIC を替えても Z440 に移しても、この値は変わらない - **公開する AAAA は静的な `::2`。** cloudflare-ddns の `local.iface.stable:bond0` は一時アドレス以外から選ぶので、SLAAC のアドレスがあると そちらを書いてしまう。`bond0` は `autoconf=0`(talos/patches/cluster.yaml)で SLAAC を付けない - 公開の中身は DMZ と同じ(IPv6 すべて・ポート any)。ノードで受けているものは IPv6 でも全部外に出る @@ -95,4 +93,4 @@ Web も、AdGuard を使う端末(端末側で指定したもの・NetBird の | Guard | アプリの「Remote Access Protection」は無効にしておく(有効だと、転送したポートも許可するまで止まる) | **アプリで 10.10.0.2(OD00)を予約し、TCP 80 / 443 をそこへ転送している。** `main` が落ちたときの入口の -[mado](../mado/)(`md.doany.io`)のため。同じ日に MeshCentral(`mc.doany.io`)を試して外し、mado に替えた。 +[mado](../mado/)(`md.doany.io`)のため。 diff --git a/docs/talos.md b/docs/talos.md index e533b13..37e787d 100644 --- a/docs/talos.md +++ b/docs/talos.md @@ -2,7 +2,7 @@ 対象: HP ProLiant DL360 Gen9(UEFI、iLO は 10.0.0.3、Xeon E5-2696 v4、RDIMM は 6 枚中 5 枚で 40 GB、NIC は BCM5719 ×4)。 ディスクは **SATA の SSD(Radeon R7、447 GB)を Smart Array(hpsa)越しに**見ている。Linux からは `/dev/sda`(`TRAN=sas` と出るが SAS ではない)。 -版は [talos/versions.yaml](../talos/versions.yaml) が正(2026-10-17 時点で v1.15.0-alpha.1 / Kubernetes v1.37.1)。 +版は [talos/versions.yaml](../talos/versions.yaml) が正。 入れ方は [recovery/README.md](../recovery/README.md)、組み立てと当て直しは [talos/README.md](../talos/README.md)。 ## Image Factory の schematic @@ -11,8 +11,7 @@ Talos は必要な system extension を焼き込んだイメージを https://fa 組み合わせは **schematic** という YAML で表し、その内容のハッシュが schematic ID になる(同じ YAML なら誰が作っても同じ ID)。 この箱に要る extension は intel-ucode と dvb-pt3(BCM5719 の tg3、bonding、AHCI は Talos のカーネルに入っている)。 -dvb-pt3 は PT3 チューナーのドライバで、**v1.15.0-alpha.1 から**ある([decisions.md](decisions.md)「PT3 チューナー」)。 -PT3 のために本番を alpha に乗せている。 +dvb-pt3 は PT3 チューナーのドライバ([decisions.md](decisions.md)「PT3 チューナー」)。 **カーネル引数もここに入れる。** v1.14 以降は `machine.install` が `UnattendedInstallConfig` と衝突して machine config 側に 書けないため。 @@ -102,8 +101,7 @@ talosctl apply-config --insecure -n -f controlplane.yaml talosctl bootstrap --recover-from=./etcd.snapshot # etcd が上がるまで `bootstrap is not available yet`。数分待って再試行 ``` -2026-09-06 に空のディスクから通した: Node が同じ名前・同じ作成時刻で Ready に戻り、Namespace・Deployment・PVC/PV の紐付け・ -PSA のラベルまで戻った。**PV の中身は戻らない**(etcd は「PVC がこの PV に紐づいている」という事実しか持っていない)ので、 +**PV の中身は戻らない**(etcd は「PVC がこの PV に紐づいている」という事実しか持っていない)ので、 このあと PV データを k8up で戻す。順序は etcd が先(PV オブジェクトが無いと戻す先が決まらない)。 **主経路ではない。** 主経路は git から建て直すほう([recovery/README.md](../recovery/README.md)。スナップショットの鮮度に左右されない)。 @@ -115,18 +113,15 @@ restic snapshots --host etcd restic dump /work/etcd.snapshot > etcd.snapshot ``` -## inlineManifests は「更新できない」ではない(2026-09-08、VM で実測) +## inlineManifests は「更新できない」ではない(VM で実測) Sidero のドキュメントの「Talos only creates missing resources from inline manifests」は起動時の適用の話で、 **`talosctl apply-config` では更新も削除もされないが、`talosctl upgrade-k8s`(同じ版でも)を通せば SSA とインベントリで更新も削除もされる** -(prune は既定で有効)。プローブ用の `ConfigMap` で確かめた。この実測を根拠に `bootstrap/` の起動に要る層(Cilium・cert-manager・ArgoCD・Infisical と SOPS の Secret)を inlineManifests に載せた。 -残りの平文は ArgoCD の Application `bootstrap` が当てる(2026-10-17 から。[bootstrap/README.md](../bootstrap/README.md))。 -当て直しの手順は [talos/README.md](../talos/README.md)「上げ方 / 当て直し方」。 +(prune は既定で有効)。当て直しの手順は [talos/README.md](../talos/README.md)「上げ方 / 当て直し方」。 ## VM ブートドリル -`talos/patches/` を QEMU で起動して確かめる。2026-09-06〜10-17 に手で 7 回回し、いまは -[.github/workflows/talos-boot.yml](../.github/workflows/talos-boot.yml) が PR ごとに描いた machine config を起動して見る。 +[.github/workflows/talos-boot.yml](../.github/workflows/talos-boot.yml) が PR ごとに描いた machine config を QEMU で起動して見る。 見つけた穴は直した場所(`talos/patches/`・`render.sh`・`add-namespace.awk`)に理由を書いてある。 VM では確かめられないもの: PT3 のモジュールの読み順、bond のフェイルオーバーの復旧方向(落とす方向は 8 秒以内で `talosctl` は途切れない)。 @@ -142,9 +137,8 @@ Z440 に移すときに変わるもの(NIC の名前・ディスク・AMT・GPU) Talos の既定(Flannel + kube-proxy)を止めて **Cilium に替えてある**(decisions.md「ルーティングの選定」。 CNI の交換は構築時にやる ── 稼働中の差し替えは全 Pod 再起動が前提)。Cilium の値は [bootstrap/cilium/values.yaml](../bootstrap/cilium/values.yaml)。 +LoadBalancer とクライアント IP も decisions.md「ルーティングの選定」。 -- **LoadBalancer の実体は無い。** hostPort と Gateway の hostNetwork で済ませた(decisions.md「LoadBalancer をどう置き換えるか」) -- **クライアント IP**: 単一ノードなので `externalTrafficPolicy: Cluster` のままで保たれる(decisions.md「クライアント IP」) - `NetworkDefaultActionConfig: block` を使うなら、`NetworkRuleConfig` で要るポートを明示的に開ける ## 管理モデル(3 つのレイヤーを混同しない) diff --git a/docs/z440-migration.md b/docs/z440-migration.md index 1a31692..00a3118 100644 --- a/docs/z440-migration.md +++ b/docs/z440-migration.md @@ -1,6 +1,6 @@ # Talos に移したノード main を SSD ごと Z440 に移す -DL360 Gen9 で動いている `main`(2026-10-17 から Talos)を、**SSD ごと** HP Z440 に移す計画(ROADMAP の Phase 2.5)。 +DL360 Gen9 で動いている `main`を、**SSD ごと** HP Z440 に移す計画(ROADMAP の Phase 2.5)。 あとで小さいサーバ(Talos)に 10.0 と 10.10 のあいだのルーターを任せ、Z440 は 10.0 側だけにする予定(「終わったら」)。 ## 構成と、Z440 での扱い @@ -12,7 +12,7 @@ DL360 Gen9 で動いている `main`(2026-10-17 から Talos)を、**SSD ごと* | SSD | SATA の Radeon R7 447GB 1 本。Smart Array 経由(素通し)なので Linux からは `TRAN=sas`・`/dev/sda` に見える | 内蔵の SATA(AHCI)に直結。`ahci` は Talos のカーネルに入っている([talos.md](talos.md)) | | 起動 | UEFI、セキュアブート無効 | **UEFI、CSM 無効**(Arc の条件)、セキュアブート無効 | | GPU | 無し(画面は iLO の Matrox G200) | **Intel Arc A310 を常設。** Xeon E5 は内蔵 GPU が無く、**Z440 は GPU が無いと POST しない**(画面なしで起動する BIOS の設定は見つからない。兄弟機には隠しの「Headless Mode」があるらしいが Z440 では未確認)。denpa の AV1 のハードウェアエンコードにも使う。Talos では extension が要る(`siderolabs/i915`。**要確認**) | -| NIC | Broadcom BCM5719 × 4(`tg3`)。`eno1`+`eno2` → `bond0`(10.0.0.2、`240f:6d:842b:1::2`)、`eno4` → 10.10.0.4 | 内蔵の Intel I218-LM 1 ポート(`e1000e`)+ **HP 361T**(Intel I350-T2、`igb`。2026-10-11 に購入)。どちらも Talos のカーネルに入っているはず(**要確認**。talos.md に書いてあるのは tg3・bonding・AHCI だけ) | +| NIC | Broadcom BCM5719 × 4(`tg3`)。`eno1`+`eno2` → `bond0`(10.0.0.2、`240f:6d:842b:1::2`)、`eno4` → 10.10.0.4 | 内蔵の Intel I218-LM 1 ポート(`e1000e`)+ **HP 361T**(Intel I350-T2、`igb`)。どちらも Talos のカーネルに入っているはず(**要確認**。talos.md に書いてあるのは tg3・bonding・AHCI だけ) | | TV チューナー | PT3(PCIe、`earth_pt3`。extension `siderolabs/dvb-pt3`) | PCIe のスロットに差し替える | | USB | B-CAS 用の IC カードリーダー(GemPC Twin)、キーボードの受信機 | 差し替える | | リモート管理 | iLO(10.0.0.3) | **Intel AMT 9.x**(ME 9.1)。電源とシリアルコンソールは使えるが、画面(KVM)は使えない(「3. リモート管理: Intel AMT」) | @@ -99,9 +99,7 @@ AMT の SOL は、OS からは PCI のシリアルポート(「Intel … KT Cont - **Windows**: Intel の [Device Management Toolkit の Console](https://github.com/device-management-toolkit/console) を使う。 Releases の `console_windows_x64.exe` を落として実行するだけ(Web の UI は同梱。初回だけ `Key Not Found, Generate new key? Y/N` に `Y`)。 10.0.0.5 を足して、電源の操作・SOL・IDE-R(ISO をつなぐ)を使う。**常駐させない**(使うときだけ手元で起動する) - - 選んだ理由(2026-10-17 に調べた): Intel Manageability Commander はサポート終了(2022 年の 2.4 が最後、新しい AMT では動かない報告あり)。 - Intel EMA は AMT 11.8.79 以降が対象で Windows Server + SQL Server が要る(AMT 9 は対象外)。MeshCommander・MeshCentral は - Intel の公式から外れた(MeshCentral は独立した OSS として続いている)。Console は Intel の公式で開発中 + - 選んだ理由: Intel の公式で開発中なのはこれだけ(Manageability Commander は終了、EMA は AMT 9 が対象外) - **未確認**: Console は README で「アルファ版、本番では使わない」としていて、対応する AMT の版も書いていない。**AMT 9 につながるか、 UEFI(Arc のため CSM 無効)のまま IDE-R の ISO から起動できるかは、AMT の初期設定のあとで最初に試す**。 つながらなければ [MeshCentral](https://github.com/Ylianst/MeshCentral) を手元で一時的に動かす。IDE-R がだめでも USB を挿せば済む diff --git a/mado/README.md b/mado/README.md index 2dc93a3..298a4c1 100644 --- a/mado/README.md +++ b/mado/README.md @@ -2,11 +2,8 @@ **`main` が落ちたときの入口。** JCOM 側の PC(10.10.0.2、手元の Windows)で [mado](https://github.com/danything/mado) (OIDC でログインしてブラウザから RDP する、自作の小さなゲートウェイ。.NET の Windows サービスとタスクトレイ)を動かし、`https://md.doany.io` から -この PC の画面に入る(2026-10-11)。入ったら、この PC から iLO(10.0.0.3)や Z440 の AMT(10.0.0.5)を開いて `main` を直す。 - -それまでは NetBird(`main` のクラスタの中の routing peer)で LAN に入り、この PC に RDP していた。NetBird の管理サーバーも -routing peer も `main` にあるので、`main` が落ちると入れず、JCOM のルーターで 3389 を開けていた。MeshCentral を -試したが、部品の多さと見た目でやめた(#333〜#338)。 +この PC の画面に入る。入ったら、この PC から iLO(10.0.0.3)や Z440 の AMT(10.0.0.5)を開いて `main` を直す。 +NetBird は管理サーバーも routing peer も `main` の上なので、`main` が落ちると使えない。 | | | | --- | --- | @@ -18,7 +15,7 @@ routing peer も `main` にあるので、`main` が落ちると入れず、JCOM | 証明書 | mado が Let's Encrypt から自分で取る(80 で HTTP-01)。30 日を切ったら取り直す | | ログイン | Entra の共用のアプリ登録(戻り先 `https://md.doany.io/callback` は [pulumi/entra.ts](../pulumi/entra.ts))。アプリロール `admin` が無いと入れない(ほかのアプリと共通) | | 設定 | [mado.json](mado.json)。`targets` がつなぐ先(画面で選ぶ。今はこの PC だけ。10.0 側の PC は、この PC の Wi-Fi がつながっているときだけ届く)。秘密は書かない(client secret は `setup.ps1` が Infisical から取り出して `client-secret.txt` に置く)。`required_role` の `admin` はほかのアプリと共通([docs/entra.md](../docs/entra.md)) | -| 版 | **mado が自分で上げる**(6 時間ごとに GitHub のリリースを見て、ECDSA の署名を確かめた MSI を、誰もつないでいないときに入れる。v0.5.0 から)。タスクトレイの「今すぐ更新を確かめる」でもよい。`setup.ps1` は版を固定せず、流したときの最新のリリースを入れる(`gh release view`)。動いているほうが新しければ MSI は入れない | +| 版 | **mado が自分で上げる**(6 時間ごとに GitHub のリリースを見て、ECDSA の署名を確かめた MSI を、誰もつないでいないときに入れる)。タスクトレイの「今すぐ更新を確かめる」でもよい。`setup.ps1` は版を固定せず、流したときの最新のリリースを入れる(`gh release view`)。動いているほうが新しければ MSI は入れない | ## 入れ方 @@ -29,7 +26,7 @@ pwsh -File mado/setup.ps1 普段のユーザーで流す。Infisical の `/shared/entra` から client secret を取り出し、リリースの `mado.msi` を落として SHA-256 と出どころ(`gh attestation verify`)を確かめたあと、残りを UAC で昇格して流す (`C:\ProgramData\mado` に設定と client secret を置く → MSI を入れる → サービスを起動し直す。IPv6 のアドレス、60fps、スリープ無効も)。 -何度流してもよい。v0.3 まで(bun 版、`C:\mado` とタスク スケジューラ)からの引っ越しもする(証明書と鍵を持ってきて、古いタスクと `C:\mado` を消す)。 +何度流してもよい。 **client secret を回したときも流し直す。** ## 使い方 diff --git a/pulumi/README.md b/pulumi/README.md index b635c9b..4a760cf 100644 --- a/pulumi/README.md +++ b/pulumi/README.md @@ -2,8 +2,8 @@ Cloudflare・Entra ID・NetBird の設定を [Pulumi](https://www.pulumi.com/) で持つ。主なもの: -- **R2 の `doany-restic`**(restic の置き場。2026-10-05)── [index.ts](index.ts) -- **Entra のアプリ登録(共用の 1 つ)のリダイレクト URI**(2026-10-06)── [entra.ts](entra.ts)。 +- **R2 の `doany-restic`**(restic の置き場)── [index.ts](index.ts) +- **Entra のアプリ登録(共用の 1 つ)のリダイレクト URI** ── [entra.ts](entra.ts)。 **アプリを足す・消すときは、そのアプリのマニフェストと同じ PR で entra.ts も直す** | いつ | 何が起きるか | 鍵 | @@ -39,12 +39,12 @@ Pulumi で表せないものは [report.ts](report.ts) が毎週見る。 **承知していること:** preview の鍵も state バケットには書ける(state のロックに要る)。同じリポジトリから PR を出せる人は、ワークフローを書き換えて state をいじれる。ただし R2 の設定(バックアップのバケット)は -読めるだけで変えられない。書けるのは本人だけ、という前提で受け入れている(2026-10-05)。 +読めるだけで変えられない。書けるのは本人だけ、という前提で受け入れている。 state は R2 の別バケット **`doany-pulumi`**(APAC)にあり、パスフレーズで暗号化している。 **state に秘密は入れていない**(アカウント ID は環境変数で渡す)ので、パスフレーズを失くしても `pulumi stack init` からやり直せる。`doany-restic` は `import` で取り込み直される。 -`doany-pulumi` 自体は Pulumi で持っていない(state の置き場を state で持つと鶏卵になる)。2026-10-05 に `cf` で作った。 +`doany-pulumi` 自体は Pulumi で持っていない(state の置き場を state で持つと鶏卵になる)。 | どこ | 何 | | --- | --- | @@ -63,7 +63,7 @@ state は R2 の別バケット **`doany-pulumi`**(APAC)にあり、パスフレ ## DNS とゾーンの設定 [dns.ts](dns.ts)。doany.io の DNS のうち **ExternalDNS が作らないもの**(ワイルドカード・メール・CAA・ドメインの確認)と、 -ゾーンの設定(SSL・常に HTTPS・最低の TLS の版・HSTS)。2026-10-07 に手で作ってあったものを import で取り込んだ。 +ゾーンの設定(SSL・常に HTTPS・最低の TLS の版・HSTS)。 - アプリごとの名前は ExternalDNS(アプリの HTTPRoute から)。apex の A / AAAA は cloudflare-ddns(家の IP が変わる)。 **どちらもここには書かない**(二重に持つと取り合いになる) @@ -73,8 +73,7 @@ state は R2 の別バケット **`doany-pulumi`**(APAC)にあり、パスフレ [notifications.ts](notifications.ts)。証明書・DDoS・オリジン不達・不正利用の報告・Security Insights を、Matrix の 部屋 `server`(スペース「通知」。hookshot の受け口 `cloudflare`)に流す。送り先の URL は hookshot の ID を含むので Actions secrets の -`CLOUDFLARE_NOTIFY_WEBHOOK_URL`(Infisical `/matrix/matrix` の `hook-cloudflare` から作る)。トークンの Notifications の -権限は 2026-10-07 に足した。 +`CLOUDFLARE_NOTIFY_WEBHOOK_URL`(Infisical `/matrix/matrix` の `hook-cloudflare` から作る)。 ## Entra @@ -88,20 +87,20 @@ CI 用のアプリ登録を 2 つに分けてある(R2 の preview / apply と **フェデレーションの subject は番号入りの形**(`repo:danything@143234231/gitops@1311609465:pull_request` など)。 このリポジトリは「変わらない subject」(`use_immutable_subject`)を使っていて、組織やリポジトリの名前が変わっても -信頼が崩れない。`repo:danything/gitops:…` で登録すると `AADSTS700213` で入れない(2026-10-06 に踏んだ)。 +信頼が崩れない。`repo:danything/gitops:…` で登録すると `AADSTS700213` で入れない。 今の形は `gh api repos/danything/gitops/actions/oidc/customization/sub` の `sub_claim_prefix`。 apply 用は「自分が所有者のアプリ登録」しか書けない。所有者になっているのは、共用のアプリ登録(`b0fa498f-…`)と CI 用の 2 つ。 テナント ID はリポジトリの Variables `AZURE_TENANT_ID`。 持っているのは**リダイレクト URI の一覧**と**アプリロール Admins の定義**(entra.ts)と、**CI 用のアプリ登録 2 つの信頼の設定**(entra-ci.ts。 -2026-10-07 に取り込んだ。apply のサービスプリンシパルを 2 つの所有者にして書けるようにした。消せないよう protect)。クライアントシークレット・ +消せないよう protect)。クライアントシークレット・ ロールの割り当ては Pulumi の外(`docs/entra.md`)。手元で流すときは `az login` のログインがそのまま使われる。 ## NetBird [netbird.ts](netbird.ts)。NetBird (https://nb.doany.io) の **DNS (ネームサーバー・ゾーン・レコード)、自宅の LAN への経路 -(ネットワーク・ルーター・リソース)、exit ノードのルート、アクセスのポリシー**。2026-10-07 に画面で作ってあったものを import で取り込んだ。 +(ネットワーク・ルーター・リソース)、exit ノードのルート、アクセスのポリシー**。 **画面で変えると、次の週次の preview (--refresh) がずれとして落ちる。** - **持っていないもの:** グループの中身 (ピアはセットアップキーやログインで入る。ID で指すだけ)、ピア、ユーザー、 @@ -111,11 +110,11 @@ apply 用は「自分が所有者のアプリ登録」しか書けない。所 SDK (`sdks/`) は **`pulumi install` が作る** (CI も毎回作る)。`bun install` だけでは SDK が無いので、 clone したら最初に `pulumi install`。版は Renovate が上げる (自動マージしない) - **Renovate は `bun.lock` を自分では作らない。** `package.json` は SDK を `file:sdks/netbird` で指していて、SDK を作れない - Renovate の `bun install` は落ちる(#304 で「Artifact update failure」になった)。renovate.json で pulumi/ の lock の更新を + Renovate の `bun install` は落ちる。renovate.json で pulumi/ の lock の更新を 止め(`skipArtifactsUpdate: true`)、代わりに Renovate の `postUpgradeTasks` が [renovate-post-upgrade.sh](renovate-post-upgrade.sh) を流す。Pulumi の CLI(`.pulumi-version`、公式のチェックサムと照らす)と bun(`.bun-version`)を入れて `pulumi install --no-plugins` し、作り直した bun.lock を同じ枝にコミットさせる。手元・CI と同じ `pulumi install` で作るので、CI の lock の確認とずれない。 - 実行の許可は [apps/renovate/renovate.yaml](../apps/renovate/renovate.yaml) の `RENOVATE_ALLOWED_COMMANDS`(2026-10-08。#306 の SDK の package.json の写しはやめた) + 実行の許可は [apps/renovate/renovate.yaml](../apps/renovate/renovate.yaml) の `RENOVATE_ALLOWED_COMMANDS` - `*.doany.io` を家の 10.0.0.2 に向けているのは、exit ノード越しに家の外向きの IPv4 を引くと折り返しで届かないため。 `nb.doany.io` だけは外向きのアドレスで例外にしている。アドレスは書かず、2 段の名前 `nb.origin.doany.io` (ゾーンのワイルドカードに当たらず Cloudflare で引かれ、cloudflare-ddns が追従させる) に CNAME で向ける @@ -154,10 +153,3 @@ node を探しに行く)。**`cf` の出力は API の封筒を外した `result | SDK・provider・`cf` | [bun.lock](bun.lock) | Renovate | | NetBird の provider と包むプラグイン | [Pulumi.yaml](Pulumi.yaml) の `packages` | Renovate (自動マージしない) | | bun | [.bun-version](.bun-version) | Renovate | - -## 経緯 - -2026-10-05 まで、ここは `r2/` で、`drift.ts` が `cf` で実物を読んで突き合わせるだけだった -(当てるのは手で、という方針)。本人の判断で「PR で差分、マージで当てる」に切り替えた。 -`doany-restic` の設定はそのとき実物と同じ内容で書き、バケットは `import` で取り込んだ -(lifecycle・ロック・r2.dev は import できないリソースなので、最初の `up` で同じ内容を書き直した)。 diff --git a/recovery/README.md b/recovery/README.md index 7409a3a..5b659e8 100644 --- a/recovery/README.md +++ b/recovery/README.md @@ -13,9 +13,8 @@ **2 つの道具は、2026-10-17 に使ったものから決め打ちを外した形で、この形ではまだ流していない。** 次の建て直しで最初に確かめる。 -**手動のリハーサルは回さない。** 「バックアップが戻せる中身か」は月次の CronJob が確かめる -([../apps/k8up/restore-check.yaml](../apps/k8up/restore-check.yaml))。「描いた machine config で起動するか」は -[../.github/workflows/talos-boot.yml](../.github/workflows/talos-boot.yml) が QEMU で確かめる。残るのはこの手順書の通し(道具そのもの)だけ。 +**手動のリハーサルは回さない。** 中身は月次の [restore-check](../apps/k8up/restore-check.yaml)、machine config は +[talos-boot](../.github/workflows/talos-boot.yml) が確かめる。残るのはこの手順書の通し(道具そのもの)だけ。 手で持つのは **age のパスフレーズ 1 つだけ**。GitHub へのログインは要らない (置き場の考え方は [../docs/decisions.md](../docs/decisions.md)「そのパスフレーズをどこに置くか」)。 @@ -122,9 +121,6 @@ kubectl -n argocd get application bootstrap # InfisicalSecret の CRD が来 kubectl get certificate -A # Gateway に証明書が付く ``` -Gateway の Service の nodePort はランダムな値のままでよい(2026-10-18 まではここで 80/443 に揃えていた。 -[../bootstrap/cilium/values.yaml](../bootstrap/cilium/values.yaml) の `nodePort`)。 - Argo CD はこの間に `apps/` と各リポジトリの `deploy/` を同期しはじめる。**中身はまだ空**なので、 Infisical の Secret を待つアプリと、Forgejo から来る非公開アプリ(`repos-forgejo` が `ErrorOccurred`)は上がらない。想定どおり。 diff --git a/talos/README.md b/talos/README.md index 9b3548c..46547b9 100644 --- a/talos/README.md +++ b/talos/README.md @@ -31,7 +31,7 @@ talosctl gen secrets -o talos/secrets.yaml && sops -e -i talos/secrets.yaml ノード単位で持つので **namespace ごとの `imagePullSecrets` が要らない**(`machine.registries`)。 持つのは **fj.doany.io**(Forgejo のコンテナレジストリ)だけで、Forgejo のユーザー `yui` の -`read:package` だけのトークン(全リポジトリ。Forgejo の名前は `talos-registry-pull`、2026-10-18 に作り直した)。 +`read:package` だけのトークン(全リポジトリ。Forgejo の名前は `talos-registry-pull`)。 `username` は Forgejo が見ない(トークンの持ち主で決まる。`info` のままにしてあるが、何でも通る)。無いと非公開のリポジトリの イメージが引けない。ghcr.io に残るイメージは全部公開。SOPS で暗号化してある(**平文は git に入れない**。規則は [`../.sops.yaml`](../.sops.yaml)): @@ -84,8 +84,7 @@ talosctl apply-config -f talos/clusterconfig/controlplane.yaml talosctl upgrade-k8s --endpoint https://10.10.0.4:6443 # ← これが inlineManifests を reconcile する ``` -`render.sh` は出力先を丸ごと消すので、`talos/clusterconfig/` に置いたもの(以前は kubeconfig を置いていた)は消える。 -kubeconfig も talosconfig もファイルでは持たない(t.ps1 がその場で作る)。 +`render.sh` は出力先を丸ごと消すので、`talos/clusterconfig/` に置いたものは消える。 - **`apply-config` だけでは inlineManifests は動かない。** 起動時にしか読まれない - **`upgrade-k8s` は版を上げなくても reconcile する。** SSA とインベントリで**更新も削除も**する @@ -93,7 +92,7 @@ kubeconfig も talosconfig もファイルでは持たない(t.ps1 がその場 - **したがって `upgrade-k8s` の前には必ず `render.sh` → `apply-config`。** 古い machine config のまま流すと**走っている Cilium が巻き戻る** - **`timeout waiting for: [Job/argocd/argocd-redis-secret-init status: 'NotFound']` で終わったら、もう一度流す。** この Job は終わると 60 秒で消える(`ttlSecondsAfterFinished`)ので、待っている間に消えて時間切れになり、 - その後ろの Deployment まで当たらないことがある(2026-10-17、argo-cd 10.10.5 を当てたとき 1 回目は ArgoCD が古い版のままだった)。 + その後ろの Deployment まで当たらないことがある。 当て終わったら `kubectl -n argocd get deploy -o wide` でイメージの版を見る - **何も変えていなければ `apply-config --dry-run` の差分は出ない。** 描くたびに変わる値は潰してある (Hubble の証明書はクラスタの中で作る、infisical の `postgres-password` は secret-values.sops.yaml に固定、`updatedAt` は `render.sh` が落とす)。 diff --git a/tools/README.md b/tools/README.md index e6bb4f7..4dafb5f 100644 --- a/tools/README.md +++ b/tools/README.md @@ -1,9 +1,7 @@ # tools -手元で使う運用のコマンドを **コンテナで動かす**(2026-10-06 から)。手元の Windows から WSL コンテナで、 -[t.ps1](t.ps1) を通して使う(中身は [../compose.yaml](../compose.yaml) の `tools` を `wslc-compose run`)。 -版は [Dockerfile](Dockerfile) で固定し、Renovate が上げる。 -Linux 用の `tools/t`(docker compose)は、手元が Windows に移ったので消した(2026-10-07)。 +手元で使う運用のコマンドを **コンテナで動かす**。手元の Windows から WSL コンテナで、 +[t.ps1](t.ps1) を通して使う。版は [Dockerfile](Dockerfile) で固定し、Renovate が上げる。 ```powershell tools/t.ps1 cf r2 buckets list @@ -23,7 +21,7 @@ tools/t.ps1 # 引数なしならシェルに入る | restic | R2 のバックアップの中身を見る・戻す | | pulumi / bun | `pulumi/`(ふだんは CI が流す) | -**jq はここに入れず、手元に置く**(dotfiles の `init.ps1` が winget で入れる。2026-10-07)。上の出力の加工は +**jq はここに入れず、手元に置く**。上の出力の加工は `tools/t.ps1 … | jq …` のようにコンテナの外でする(コンテナを起こすほどではなく、パイプの先で使うことが多いため)。 ## 仕組み @@ -43,12 +41,11 @@ tools/t.ps1 # 引数なしならシェルに入る base64 で包んでコンテナの中で戻す)。PowerShell のパイプは行(文字列)単位なので、バイナリは通らない。 バイナリはファイルに書いてリポジトリの中から読ませる - `t.ps1` は **BOM 付きの UTF-8** にしてある。BOM が無いと Windows PowerShell 5.1 が Shift_JIS として読み、 - 日本語のコメントで構文が壊れる(2026-10-07 まで 5.1 では動かなかった) + 日本語のコメントで構文が壊れる ## クラスタの資格情報 -**kubectl・talosctl・helm は、クラスタに繋ぐときだけ資格情報をコンテナの中で作るか復号して渡す**(2026-10-17。 -[creds.sh](creds.sh))。置き場はコンテナの `/dev/shm`(tmpfs)で、コンテナと一緒に消える。 +**kubectl・talosctl・helm は、クラスタに繋ぐときだけ資格情報をコンテナの中で作るか復号して渡す**([creds.sh](creds.sh))。置き場はコンテナの `/dev/shm`(tmpfs)で、コンテナと一緒に消える。 **平文は手元のディスクにも `.home/` にも残らない。** age 鍵(上の「仕組み」)が無いと止まる。 **既定は読み取り用。管理者は `tools/t.ps1 --admin …` と書いたときだけ。** 書く操作(`apply`・`delete`・ @@ -80,14 +77,14 @@ tools/t.ps1 # 引数なしならシェルに入る `cf auth login` には対応していない - uid は指定しない(Windows のファイルに持ち主の uid は無い) - **標準入力が NUL のところ(Claude Code のツールなど)では、`wslc-compose run` がつなぐ標準入力が無効なハンドルになる。** - 1 秒ほど以上かかるコマンドの出力が落ちて `ERROR_INVALID_HANDLE` で終わるので、`t.ps1` はそのときだけ空のパイプを渡す(2026-10-07)。 + 1 秒ほど以上かかるコマンドの出力が落ちて `ERROR_INVALID_HANDLE` で終わるので、`t.ps1` はそのときだけ空のパイプを渡す。 NUL かどうかは標準入力の種類(`GetFileType`)で見分けるので、ファイルや外のパイプからの入力は今までどおり届く - NetBird の exit ノード越しだと `*.doany.io` を外向きの IPv4 で引いたときに届かないので、NetBird の DNS ゾーンで 内部の 10.0.0.2 に向けている(`nb.doany.io` だけは例外) ## 入れるものの確かめ方 -**全部、公式が出しているチェックサムと照らし合わせてから入れる**(2026-10-07)。sops は age 鍵を、infisical は秘密を扱うので、 +**全部、公式が出しているチェックサムと照らし合わせてから入れる**。sops は age 鍵を、infisical は秘密を扱うので、 取得元やミラーで差し替えられても気づけるようにする。一致しなければ組み立てごと止まる。 - kubectl・talosctl・helm・sops・infisical・restic・pulumi: 各リリースのチェックサムのファイル