Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
| | |
| --- | --- |
| `apps/` | Argo CD が再帰的に同期するアプリのマニフェスト。**`_` で始まるディレクトリは同期しない** (動かしていないものの置き場。下の「動かしていないもの」) |
| [`bootstrap/`](bootstrap/) | クラスタそのものを組む層。**ここにあるものは全部 machine config に載る**([render.sh](bootstrap/render.sh) が `helm template` して inlineManifest にする)。ノードの Talos の設定(**v1.14 以降の形**。手で書くのは [talos.sops.yaml](bootstrap/talos.sops.yaml) の 1 本。PR ごとに [talos-boot](.github/workflows/talos-boot.yml) が生成物を検証して VM で起動する)、Helm で入れるもの(Cilium・Argo CD 本体・Infisical・cert-manager。部品ごとに `<name>-values.yaml` 1 本。秘密を含むものは `<name>-values.sops.yaml` で、秘密の値だけ SOPS)、RBAC、SOPS 済みのもの |
| [`bootstrap/`](bootstrap/) | クラスタそのものを組む層。**ここにあるものは全部 machine config に載る**([render.sh](bootstrap/render.sh) が描く)。Talos の設定([talos.sops.yaml](bootstrap/talos.sops.yaml))、Helm で入れるもの(Cilium・Argo CD 本体・Infisical・cert-manager)、RBAC。PR ごとに [talos-boot](.github/workflows/talos-boot.yml) が VM で起動して確かめる |
| [`pulumi/`](pulumi/) | Cloudflare(R2・DNS・ゾーンの設定・通知)・Entra(リダイレクト URI・CI の信頼の設定)・NetBird の設定を Pulumi で持つ。**PR で差分がコメントされ、main に入ると当たる** |
| [`mado/`](mado/) | **`main` が落ちたときの入口**。JCOM 側の PC(10.10.0.2)で [mado](https://github.com/danything/mado)(OIDC でログインしてブラウザから RDP する exe)を動かす(`md.doany.io`。クラスタの外) |
| [`recovery/`](recovery/) | **まっさらから建て直す入口**(手順・PV を戻す道具・暗号化した鍵)。git と restic だけで戻す |
| [`tools/`](tools/) + `compose.yaml` | **手元の運用のコマンド(cf・sops・infisical・kubectl・talosctl など)はコンテナで動かす**。`tools/t.ps1 <コマンド>`(Windows の WSL コンテナ。wslc-compose)。手元にコマンドは入れない |
| [`docs/`](docs/) | [決定の記録](docs/decisions.md)、[Talos の作法と検証](docs/talos.md)、[Entra ID の認可](docs/entra.md)、[家のルーター](docs/router.md)、[Z440 への移行](docs/z440-migration.md)、[Cloudflare の API トークン](docs/cloudflare-tokens.md) |
| [`ROADMAP.md`](ROADMAP.md) | やることと進捗(なぜそうしたかは `docs/decisions.md`) |
| `deploy/argocd.yaml` | このリポジトリ自身の Application 定義(他のリポジトリと同じ場所) |
| [`.sops.yaml`](.sops.yaml) | 平文の秘密を SOPS(age)で暗号化する規則(`bootstrap/` の `*.sops.yaml` の 6 つ) |
| [`.sops.yaml`](.sops.yaml) | `bootstrap/*.sops.yaml` を SOPS(age)で暗号化する規則 |

## 秘密の扱い

Expand All @@ -35,10 +35,10 @@
そのまま Secret のキーになる。値を変えるのは Infisical の UI だけでよく、`secrets.infisical.com/auto-reload: "true"`
の注釈がある Deployment は Pod ごと入れ替わる。詳しくは [`bootstrap/README.md`](bootstrap/README.md)。

**Infisical より下の層だけは Infisical から取れない**ので、`bootstrap/` の 4 ファイル(Infisical 自身の鍵、
Argo CD が git を読む GitHub App 鍵、cert-manager の Cloudflare トークン)は **SOPS + age** で該当キーだけ暗号化してある。
**Infisical より下の層だけは Infisical から取れない**ので、`bootstrap/*.sops.yaml`(クラスタの CA、Infisical 自身の鍵、
Argo CD が git を読む GitHub App 鍵、cert-manager の Cloudflare トークン、fj.doany.io の資格情報など)は **SOPS + age** で暗号化してある。
鍵は [`recovery/sops-age.key.age`](recovery/)(バックアップの `env.age` と同じパスフレーズ)。
当てるのは machine config(`bootstrap/render.sh` が復号して載せる)。
当てるのは machine config(`bootstrap/render.sh` が復号して載せる)。書き方は [`bootstrap/README.md`](bootstrap/README.md)。

```shell
# 鍵はリポジトリの .home/(git に入らない)に置く。コマンドは tools/t.ps1 で動かす(tools/README.md)
Expand Down Expand Up @@ -83,17 +83,15 @@ headlamp が 1 文字なのは、規則どおりだと head + lamp でも hubble
| [`3proxy/`](apps/3proxy/) | 3proxy (国内IP経由の HTTPS フォワードプロキシ) |
| [`k8up/`](apps/k8up/) | バックアップ(restic → Cloudflare R2)。PVC・DB のダンプ・etcd のスナップショット |
| [`infisical/`](apps/infisical/) | 秘密の配布。本体は `bootstrap/` にあり、ここには公開経路と operator と即時反映のブリッジと SMTP のパスワード |
| [`cluster/`](apps/cluster/) | ノードまわりの小物。metrics-server(Talos に無い。`kubectl top` と各 UI の使用量表示)と、PT3 をドライバに付け直す DaemonSet |
| [`cluster/`](apps/cluster/) | metrics-server(Talos に無い。`kubectl top` と各 UI の使用量表示) |
| [`smtp-relay/`](apps/smtp-relay/) | メールの中継。アプリの SMTP を受けて Microsoft 365 へ Graph で送る(Exchange Online の SMTP AUTH の Basic 認証の廃止に備えて)。**送る側の設定は [apps/smtp-relay/README.md](apps/smtp-relay/README.md)** |
| [`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`)。認証は Entra、権限は `bootstrap/apiserver-rbac.yaml` |
| [`rybbit/`](apps/rybbit/) | Rybbit(`rt.doany.io`、アクセス解析。クッキー無し)+ ClickHouse + PostgreSQL + Redis。トドロクの離脱をファネル・ジャーニー・セッションリプレイで見る。**使い始めは [apps/rybbit/README.md](apps/rybbit/README.md)** |

**動かしていないもの**は `apps/_<name>/` に置いてある。`_` で始まるディレクトリは Argo CD が同期しない
([bootstrap/argocd-repos.yaml](bootstrap/argocd-repos.yaml) の `directory.exclude`。全リポジトリ共通の決まり)ので、
クラスタには何も作らない。動かすときは `git mv apps/_<name> apps/<name>`。いまは無い。
**動かしていないもの**は `apps/_<name>/` に置く。`_` で始まるディレクトリは Argo CD が同期しない
([bootstrap/argocd-repos.yaml](bootstrap/argocd-repos.yaml) の `directory.exclude`。全リポジトリ共通の決まり)。
動かすときは `git mv apps/_<name> apps/<name>`。いまは無い。

**アプリの多くはこのリポジトリに無い。** 各アプリのリポジトリの `deploy/` に置いてあり、
`bootstrap/argocd-repos.yaml` の ApplicationSet が拾う(上の「仕組み」)。
`kubectl -n argocd get applications` が実際の一覧。
**アプリの多くはこのリポジトリに無い**(各リポジトリの `deploy/`。上の「仕組み」)。実際の一覧は `kubectl -n argocd get applications`。

10 changes: 4 additions & 6 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,12 @@ bond0 の MAC は設定で固定してあるのでルーターは触らない。
## Phase 3 — Talos 定常運用

- [ ] **Talos 1.15.0 の正式版が出たら乗り換える**(Renovate の PR)
- [x] **上流がカーネルにモジュールを必要な時に読ませる仕組み(modprobe の helper)を入れたら**、pt3-rebind を外し、
`bootstrap/talos.sops.yaml` の `kernel.modules` を `earth-pt3` だけにする。並び順を守る PR([siderolabs/talos#14652](https://github.com/siderolabs/talos/pull/14652))は
2026-10-23 に「並び順ではなく、カーネルが必要な時に読み込む形にすべき。こちらで別に実装する」(smira)として閉じられ、
その実装 [siderolabs/talos#14724](https://github.com/siderolabs/talos/pull/14724) が 2026-10-24 にマージされて v1.15.0-alpha.2 に入った。
2026-10-24 に v1.15.0-alpha.1 + この PR を自前で組んだ installer で実機を起こし、`earth-pt3` だけでチューナー 4 つが付くことは確かめてあった。
alpha.2 に上げる PR(#607)で pt3-rebind を消し、`kernel.modules` を `earth-pt3` だけにした。2026-10-29 に公式の alpha.2 へ upgrade し、earth_pt3 から tc90522・qm1d1c0042・mxl301rf が自動で読まれてチューナー 4 つ(adapter0〜3 と frontend0)が付くのを確かめた。Kubernetes も 1.37.2 になった

## 済んだこと(記録)

- **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))
- **PT3 の付け直し(pt3-rebind)を外した(2026-10-29)。** 並び順を守る PR([siderolabs/talos#14652](https://github.com/siderolabs/talos/pull/14652))は
「カーネルが必要な時に読み込む形にすべき」(smira)として閉じられ、その実装 [siderolabs/talos#14724](https://github.com/siderolabs/talos/pull/14724) が
v1.15.0-alpha.2 に入った。#607 で `kernel.modules` を `earth-pt3` だけにし、alpha.2(Kubernetes 1.37.2)に上げて、
tc90522・qm1d1c0042・mxl301rf が自動で読まれてチューナー 4 つ(adapter0〜3 と frontend0)が付くのを確かめた
2 changes: 1 addition & 1 deletion apps/external-dns/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ metadata:
- アプリから見える接続元は Cloudflare の IP。本当の接続元は `CF-Connecting-IP`(Rybbit は自分で読む)
- 応答が 100 秒を超えると 524、リクエストの本文は 100MB まで(無料プラン)
- SSL は Full (strict)(`pulumi/dns.ts` の zone 設定)。Gateway の Let's Encrypt の証明書で足りる
- NetBird につないでいる端末は、NetBird の DNS(`pulumi/netbird.ts`)で `*.doany.io` を 10.0.0.2 に引くので Cloudflare を通らない
- NetBird につないでいる端末は、NetBird の DNS(`pulumi/netbird.ts`)で `*.doany.io` を 10.0.0.2 に引くので Cloudflare を通らない(`nb`・`md`・`autodiscover` を除く)

## 触るもの・触らないもの

Expand Down
2 changes: 1 addition & 1 deletion apps/k8up/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
| forgejo | postgres の `pg_dump` + リポジトリの PVC(ファイル) | [../forgejo/postgres.yaml](../forgejo/postgres.yaml) |
| rybbit | postgres の `pg_dump` + ClickHouse の PVC(ファイル。パートは追記だけなので動いたまま取る) | [../rybbit/postgres.yaml](../rybbit/postgres.yaml) / [../rybbit/clickhouse.yaml](../rybbit/clickhouse.yaml) |
| infisical | postgres の `pg_dump` | `bootstrap/infisical-values.sops.yaml` の `postgresql.primary.podAnnotations` |
| lgtm / xool / worklog / denpa / blog / todoroku | SQLite を `serialize()` した 1 ファイル | 各アプリのリポジトリの `deploy/`(denpa と yosegaki は chart) |
| lgtm / xool / worklog / denpa / yosegaki / todoroku | SQLite を `serialize()` した 1 ファイル | 各アプリのリポジトリの `deploy/`(denpa と yosegaki は chart) |
| netbird | `store.db` / `idp.db` / `events.db` を tar 1 本に(bun のサイドカー) | [../netbird/deployment.yaml](../netbird/deployment.yaml) |
| matrix | Tuwunel 自身のバックアップと `media/` を tar 1 本に(DB は動いたまま写さない) | [../matrix/tuwunel.yaml](../matrix/tuwunel.yaml) |
| adguardhome / yuzuriha / ashi | ファイルだけ(下の表) | ─ |
Expand Down
2 changes: 1 addition & 1 deletion apps/netbird/routing-peer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ spec:
- name: data
mountPath: /var/lib/netbird
# カーネルの wireguard が使えないときのユーザ空間フォールバック用。
# Talos でもカーネル組み込みは確認済み(bootstrap/talos.md)なので通常は使わない
# Talos でもカーネル組み込みは確認済みなので通常は使わない
- name: tun
mountPath: /dev/net/tun
volumes:
Expand Down
42 changes: 16 additions & 26 deletions bootstrap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ Namespace は chart が描かないので、`render.sh` が kube-system 以外

## git と live がずれていないか

**cilium は [cilium-drift.yml](../.github/workflows/cilium-drift.yml) が週次で見ている**(値が `cilium-config` という
ConfigMap に出ているので、読むだけの権限で比べられる)。**残りは見ていない。** 当て忘れは `upgrade-k8s` の差分に出るので、
**cilium だけは [cilium-drift.yml](../.github/workflows/cilium-drift.yml) が見ている**(下の「cilium」)。**残りは見ていない。**
直したら流す(流し忘れたことは誰も教えてくれない)。描くたびに変わる値は潰してあるので、
`apply-config --dry-run` に出る差分は全部本物([talos.md](talos.md)「inlineManifests を直したとき」)。

Expand All @@ -89,18 +88,13 @@ ConfigMap に出ているので、読むだけの権限で比べられる)。**
`render.sh` がここから描く派生物(二度書かない。decisions.md「machine config と Cilium の chart をどう連動させるか」)。
**どの設定が何のために要るかは [cilium-values.yaml](cilium-values.yaml) のコメントにある**(ここには写さない)。

### 描き直すたびに Hubble の証明書が入れ替わる

チャートの `hubble/tls-helm/*` は毎回 `genCA` / `genSignedCert` で作り直す
(`cilium.io/helm-template-non-idempotent: "true"`)。**値を 1 行も変えなくても、`hubble-ca-secret` /
`hubble-server-certs` / `hubble-relay-client-certs` の 3 つは `upgrade-k8s` の差分に出る。**
実害は無い(エージェントも Relay もファイルの入れ替わりに追従する。Pod の再起動は要らない)。
`cilium-dbg status` の `Hubble: Ok` と `hubble-relay` の Ready で確かめる。
**Hubble の証明書はクラスタの中の CronJob が作る**(`hubble.tls.auto.method: cronJob`)。chart の既定(`helm`)は描くたびに
作り直して machine config に秘密鍵が載り、値を変えなくても `upgrade-k8s` の差分に出ていた。

### ズレは CI が見ている

**当て忘れとクラスタ側の直接編集は [cilium-drift.yml](../.github/workflows/cilium-drift.yml) が見つける。**
`cilium-values.yaml` から ConfigMap を描いて live と突き合わせるだけで、**読むだけ**(権限は `cilium-config` の `get` 1 つ)。
`cilium-values.yaml` から `cilium-config` の ConfigMap を描いて live と突き合わせるだけで、**読むだけ**(権限は `cilium-config` の `get` 1 つ)。
`cilium-values.yaml` を触ったときと毎週月曜に走る。当てるのは自動化しない(Talos API の os:admin を CI に置かない)。

### 当てたあとに確認すること
Expand All @@ -115,18 +109,15 @@ kubectl -n kube-system get gateway doany
## Secret

平文の Secret はアプリのリポジトリにはコミットしない。値は **Infisical**(https://il.doany.io、このクラスタでセルフホスト)が持ち、
**Infisical 純正 operator**(`apps/infisical/operator.yaml`)が各リポジトリの `InfisicalSecret` を見て Infisical から値を取り、普通の `Secret` を作る。
認証は Kubernetes 方式(TokenReview・長命の資格情報なし)。

- `infisical-*` … Infisical 本体(chart + Postgres/Redis)。公開経路は [`apps/infisical/httproute.yaml`](../apps/infisical/httproute.yaml)。
`infisical-values.sops.yaml` の 2 つめのドキュメントの `ENCRYPTION_KEY` が DB の暗号鍵で、これを失うと Infisical の中身が全部読めなくなる
(クラスタで一番失ってはいけない値)。Postgres の PVC は既定の `local-path` で、`pg_dump` を k8up が日次で取っている。
- **operator と push-bridge はここには無い**(`apps/infisical/operator.yaml`、`apps/infisical/push-bridge.yaml`)。
ArgoCD 自身は operator に依存しないので、bootstrap に置く理由がない。
認証は Infisical の Kubernetes auth: operator が SA `infisical-auth` のトークンで Infisical にログインし、
Infisical がそのトークンで TokenReview を呼んで本物か確かめる(なので SA に `system:auth-delegator` を
付けてある)。SA と RBAC は `apps/infisical/operator-rbac.yaml`。
operator 側に長期の資格情報は無い(`identityId` は秘密ではない)。
**Infisical 純正 operator** が各リポジトリの `InfisicalSecret` を見て Infisical から値を取り、普通の `Secret` を作る。

- **本体**(chart + Postgres/Redis)だけがここ(machine config)。`infisical-values.sops.yaml` の 2 つめのドキュメントの
`ENCRYPTION_KEY` が DB の暗号鍵で、これを失うと Infisical の中身が全部読めなくなる(クラスタで一番失ってはいけない値)。
Postgres の PVC は `local-path` で、`pg_dump` を k8up が日次で取っている。
- **公開経路・operator・push-bridge は [`apps/infisical/`](../apps/infisical/)**(ArgoCD 自身は operator に依存しないので、ここに置く理由がない)。
- 認証は Infisical の Kubernetes auth: operator が SA `infisical-auth` のトークンで Infisical にログインし、
Infisical がそのトークンで TokenReview を呼んで本物か確かめる(なので SA に `system:auth-delegator` を付けてある。
`apps/infisical/operator-rbac.yaml`)。長命の資格情報はどこにも無い(`identityId` は秘密ではない)。

### API サーバの入口(GitHub Actions と人)

Expand All @@ -140,10 +131,9 @@ kubeconfig も age 鍵も GitHub に置いていない。
Headlamp が利用者本人の id_token を API サーバへ転送する。アプリロール `admin` を持つ人は
`entra:admin` で cluster-admin、それ以外は `entra:viewer` で読み取り専用(組み込みの `view`
+ CRD とノードぶんの `readonly-extra`)。**振り分けは CEL で、割り当ては Entra 側で完結する。**
ここに置いたのは、ArgoCD も CI も要らずに効く層だから ── 締め出されたときに直せる。
認証設定([talos.sops.yaml](talos.sops.yaml))も [apiserver-rbac.yaml](apiserver-rbac.yaml) も
machine config に載っているので、変えたら `render.sh` → `apply-config`(RBAC は inlineManifest なので `upgrade-k8s` まで)。
**ArgoCD には当てさせない**(締め出されたときに直せる層に置く)。
認証設定([talos.sops.yaml](talos.sops.yaml))も [apiserver-rbac.yaml](apiserver-rbac.yaml) も machine config に載せ、
**ArgoCD には当てさせない**(ArgoCD も CI も要らずに効く層なので、締め出されたときに直せる)。
変えたら `render.sh` → `apply-config`(RBAC は inlineManifest なので `upgrade-k8s` まで)。

### Infisical の中の構成

Expand Down
2 changes: 1 addition & 1 deletion bootstrap/argocd-values.sops.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ redis:
image:
repository: mirror.gcr.io/library/redis
tag: 8.10.3-alpine
# Dex を使わず Entra ID に直接 OIDC(auth/deployment.yaml と同じアプリ登録)
# Dex を使わず Entra ID に直接 OIDC(../apps/auth/deployment.yaml と同じアプリ登録)
dex:
enabled: false
configs:
Expand Down
2 changes: 1 addition & 1 deletion bootstrap/cilium-values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ gatewayAPI:
# 全リスナーが HTTP/2 になるが、WebSocket はブラウザが HTTP/1.1 に落ちるので動く。
enableAlpn: true

# UI 自身は認証を持たない。前段の oauth2-proxy(../auth/hubble-proxy.yaml)で守る。UI の Service を直接 HTTPRoute に繋がない。
# UI 自身は認証を持たない。前段の oauth2-proxy(../apps/auth/hubble-proxy.yaml)で守る。UI の Service を直接 HTTPRoute に繋がない。
hubble:
relay:
enabled: true
Expand Down
Loading
Loading