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
26 changes: 10 additions & 16 deletions .github/workflows/talos-boot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,10 @@ jobs:
run: |
set -euo pipefail
talosctl gen secrets -o /tmp/secrets.yaml
# ダミーは fj.doany.io に置く(ghcr.io に置くと kubelet の取得が 403 で落ちる)
cat > /tmp/registries.yaml <<'PATCH'
machine:
registries:
config:
fj.doany.io:
auth:
username: ci-dummy
password: ci-dummy
PATCH
# controlplane.yaml は password(fj.doany.io)だけ暗号化してある。ダミーに置き換える
# (ダミーは fj.doany.io に置いたまま。ghcr.io に置くと kubelet の取得が 403 で落ちる)
yq 'del(.sops) | (.. | select(tag == "!!str" and test("^ENC\\["))) |= "ci-dummy"' \
talos/controlplane.yaml > /tmp/controlplane.yaml
# values.yaml は本物、secret-values.sops.yaml だけダミー(通知先もそこにしか無い)。
# extraObjects は app-of-apps と repo-creds。CI で本物を同期させない
echo 'extraObjects: []' > /tmp/argocd-secret-values.yaml
Expand All @@ -116,10 +110,10 @@ jobs:
bootstrap/infisical/secrets.sops.yaml > /tmp/infisical-secret.yaml
yq 'del(.sops) | (.. | select(tag == "!!str" and test("^ENC\\["))) |= "ci-dummy"' \
bootstrap/cert-manager/cloudflare-secret.sops.yaml > /tmp/cloudflare-secret.yaml
for f in /tmp/infisical-secret.yaml /tmp/cloudflare-secret.yaml; do
for f in /tmp/controlplane.yaml /tmp/infisical-secret.yaml /tmp/cloudflare-secret.yaml; do
! grep -q 'ENC\[' "$f" || { echo "::error::$f に暗号文が残っている"; exit 1; }
done
SECRETS=/tmp/secrets.yaml REGISTRIES=/tmp/registries.yaml \
SECRETS=/tmp/secrets.yaml CONTROLPLANE=/tmp/controlplane.yaml \
ARGOCD_SECRET_VALUES=/tmp/argocd-secret-values.yaml INFISICAL_SECRET_VALUES=/tmp/infisical-secret-values.yaml \
INFISICAL_SECRET=/tmp/infisical-secret.yaml CLOUDFLARE_SECRET=/tmp/cloudflare-secret.yaml \
./talos/render.sh /tmp/out
Expand Down Expand Up @@ -256,17 +250,17 @@ jobs:
echo " volume $v: $P"
done

WANT=$(yq -r 'select(.kind == "BondConfig") | .hardwareAddr' talos/patches/cluster.yaml)
WANT=$(yq -r 'select(.kind == "BondConfig") | .hardwareAddr' talos/controlplane.yaml)
GOT=$(talosctl get links bond0 -o json | jq -r .spec.hardwareAddr)
[ "$GOT" = "$WANT" ] || fail "bond0 の MAC が $GOT(cluster.yaml は $WANT)"
[ "$GOT" = "$WANT" ] || fail "bond0 の MAC が $GOT(controlplane.yaml は $WANT)"
echo " bond0 hardwareAddr: $GOT"

# 以下は forwarding=1 のあとで見ないと意味が無い(cluster.yaml の accept_ra)
# 以下は forwarding=1 のあとで見ないと意味が無い(controlplane.yaml の accept_ra)
F=$(talosctl read /proc/sys/net/ipv6/conf/bond0/forwarding)
[ "$F" = 1 ] || fail "bond0 の forwarding が $F。前提が成り立っていない"

# グローバルな IPv6 は静的な ::2 だけ(SLAAC は autoconf=0 で止めている)
STATIC=$(yq -r 'select(.kind == "BondConfig") | .addresses[].address | select(test(":"))' talos/patches/cluster.yaml)
STATIC=$(yq -r 'select(.kind == "BondConfig") | .addresses[].address | select(test(":"))' talos/controlplane.yaml)
GLOBAL=$(talosctl get addresses -o json \
| jq -rs '.[] | select(.spec.linkName == "bond0" and .spec.family == "inet6" and .spec.scope != "link") | .spec.address' | sort)
echo " bond0 の IPv6(link-local 以外): $(echo $GLOBAL)"
Expand Down
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
talos/clusterconfig/
# **平文のまま置かないこと。** talos/secrets.yaml と controlplane.yaml は SOPS 済みのものをコミットする
# (talos/README.md)。書きかけの平文はこの名前に逃がす。
talos/secrets.plain.yaml
# **平文のまま置かないこと。** talos/registries.yaml は SOPS 済みのものをコミットする
# (talos/README.md「fj.doany.io の資格情報」)。書きかけの平文はこの名前に逃がす。
talos/registries.plain.yaml
# 平文の kubeconfig / talosconfig も置かない。どちらも tools/t.ps1 が毎回その場で作る
# (tools/README.md「クラスタの資格情報」)。talosctl kubeconfig と gen config がこの名前で書き出す
kubeconfig
Expand Down
10 changes: 7 additions & 3 deletions .sops.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ creation_rules:
- path_regex: ^bootstrap/.*\.sops\.yaml$
encrypted_regex: ^(ENCRYPTION_KEY|AUTH_SECRET|CLIENT_ID_GITHUB_LOGIN|CLIENT_SECRET_GITHUB_LOGIN|CF_DNS_API_TOKEN)$
age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r
# 以下は全部が秘密なので丸ごと
- path_regex: ^talos/secrets\.yaml$
# 手で書く machine config。秘密は password の値だけ(fj.doany.io)。平文の部分は sops を通さず直接直せるよう
# mac_only_encrypted(MAC が暗号化した値だけを見る)。SOPS はドキュメントごとに sops: を付け、インデントを 4 にする
- path_regex: ^talos/controlplane\.yaml$
encrypted_regex: ^password$
mac_only_encrypted: true
age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r
- path_regex: ^talos/registries\.yaml$
# 全部が秘密なので丸ごと(CA 一式)
- path_regex: ^talos/secrets\.yaml$
age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
| [`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 だけで戻す |
| [`talos/`](talos/) | ノードの machine config(**v1.14 以降の形**。PR ごとに [talos-boot](.github/workflows/talos-boot.yml) が生成物を検証して VM で起動する)。**`bootstrap/` のほぼ全部がここに載る** ── [render.sh](talos/render.sh) が `helm template` して inlineManifest にする |
| [`talos/`](talos/) | ノードの machine config(**v1.14 以降の形**。手で書くのは [controlplane.yaml](talos/controlplane.yaml) の 1 本。PR ごとに [talos-boot](.github/workflows/talos-boot.yml) が生成物を検証して VM で起動する)。**`bootstrap/` のほぼ全部がここに載る** ── [render.sh](talos/render.sh) が `helm template` して inlineManifest にする |
| [`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`) |
Expand Down Expand Up @@ -79,7 +79,8 @@ headlamp が 1 文字なのは、規則どおりだと head + lamp でも hubble
| [`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 と即時反映のブリッジ |
| [`infisical/`](apps/infisical/) | 秘密の配布。本体は `bootstrap/` にあり、ここには公開経路と operator と即時反映のブリッジ |
| [`cluster/`](apps/cluster/) | ノードまわりの小物。metrics-server(Talos に無い。`kubectl top` と各 UI の使用量表示)と、PT3 をドライバに付け直す DaemonSet |
| [`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/` |
| [`rybbit/`](apps/rybbit/) | Rybbit(`rt.doany.io`、アクセス解析。クッキー無し)+ ClickHouse + PostgreSQL + Redis。トドロクの離脱をファネル・ジャーニー・セッションリプレイで見る。**使い始めは [apps/rybbit/README.md](apps/rybbit/README.md)** |
Expand Down
4 changes: 2 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ bond0 の MAC は設定で固定してあるのでルーターは触らない。
- [ ] **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 も直す(上流)
([siderolabs/talos#14652](https://github.com/siderolabs/talos/pull/14652))が入ったら、[apps/cluster/pt3-rebind.yaml](apps/cluster/pt3-rebind.yaml) を外して
`talos/controlplane.yaml` の並び順だけにする。入ったら dvb-pt3 の extension の README も直す(上流)

## 済んだこと(記録)

Expand Down
2 changes: 1 addition & 1 deletion apps/cloudflare-ddns/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ spec:
domains: doany.io
proxied: "true"
# AAAA は bond0 の static な ::2。SLAAC のアドレスがあるとそちらを選ぶので、
# bond0 は autoconf=0 で SLAAC を付けない(talos/patches/cluster.yaml、docs/router.md)。
# bond0 は autoconf=0 で SLAAC を付けない(talos/controlplane.yaml、docs/router.md)。
# 外向きの probe が拾う一時アドレスではない。この provider は
# インタフェースを読んで temporary / deprecated を飛ばす。
ip6Provider: local.iface.stable:bond0
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion apps/pt3/rebind.yaml → apps/cluster/pt3-rebind.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# PT3 をドライバに付け直す(talos/patches/main.yaml の kernel.modules と対)。
# PT3 をドライバに付け直す(talos/controlplane.yaml の kernel.modules と対)。
#
# Talos は kernel.modules を書いた順ではなく名前の順に読むので、earth-pt3 がチューナー
# (qm1d1c0042 / mxl301rf)より先に入り、`Failed to create FE0` で /dev/dvb/*/frontend0 ができない。
Expand Down
2 changes: 1 addition & 1 deletion apps/headlamp/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#
# 認証は Entra の OIDC。Headlamp は利用者の id_token を API サーバへ転送するだけで、
# 権限を持たない。誰が何をできるかは apiserver 側で決まる
# (talos/patches/apiserver.yaml と bootstrap/apiserver/rbac.yaml)。アプリロール admin を
# (talos/controlplane.yaml と bootstrap/apiserver/rbac.yaml)。アプリロール admin を
# 持つ人は cluster-admin、それ以外は読み取り専用。
#
# コールバックは Headlamp が要求のホストから組み立てる(Envoy が x-forwarded-proto を
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion apps/k8up/etcd-snapshot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# (Argo CD が作った Application の同期状態、cert-manager の発行済み証明書など)まで含めて**一瞬で戻したいとき**の近道。
# 戻し方は docs/talos.md「etcd スナップショットからの復旧」の `bootstrap --recover-from`。
#
# Talos API への権限は `os:etcd:backup` だけ(talos/patches/etcd-backup.yaml)。
# Talos API への権限は `os:etcd:backup` だけ(talos/controlplane.yaml)。
# 下の ServiceAccount を見て Talos が同名の Secret に talosconfig を書く。
#
# **通知は何も足さなくてよい。** notify は過去 8 日に出てきた (host, path) を
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 @@ -66,7 +66,7 @@ spec:
app: netbird-routing-peer
spec:
# hostNetwork にしない。Pod のネットワークからでも LAN には届く(Cilium が masquerade する)。
# ホストからは公開 DNS の A(WAN 側)に届かない(talos/patches/cluster.yaml の StaticHostConfig)ので、
# ホストからは公開 DNS の A(WAN 側)に届かない(talos/controlplane.yaml の StaticHostConfig)ので、
# hostNetwork だと nb.doany.io:443 に繋げず `Management: Disconnected` で止まる。
containers:
- name: netbird
Expand Down
2 changes: 1 addition & 1 deletion apps/netbird/server-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ data:

reverseProxy:
# Envoy(Cilium Gateway)の Pod から来る。X-Forwarded-For を信じる範囲。
# Pod CIDR(talos/patches/cluster.yaml の podSubnets)。bootstrap/auth の oauth2-proxy と同じ値。
# Pod CIDR(talos/controlplane.yaml の podSubnets)。bootstrap/auth の oauth2-proxy と同じ値。
trustedHTTPProxies:
- "10.42.0.0/16"
# NetBird 自身のピア網(既定 100.64.0.0/10)
Expand Down
14 changes: 7 additions & 7 deletions bootstrap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
| **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 付き) |

`InfisicalSecret` の CRD だけは `apps/infisical-operator/` が入れるので、まっさらなクラスタでは `bootstrap` の同期が
`InfisicalSecret` の CRD だけは `apps/infisical/operator.yaml` が入れるので、まっさらなクラスタでは `bootstrap` の同期が
最初の何回か落ちて retry で戻る。

**SOPS(age)で暗号化したファイルは ArgoCD が当てない** ── 復号鍵をクラスタにも CI にも渡さないため。
Expand All @@ -34,23 +34,23 @@ ConfigMap に出ているので、読むだけの権限で比べられる)。**
## Secret

平文の Secret はアプリのリポジトリにはコミットしない。値は **Infisical**(https://il.doany.io、このクラスタでセルフホスト)が持ち、
**Infisical 純正 operator**(`apps/infisical-operator/`)が各リポジトリの `InfisicalSecret` を見て Infisical から値を取り、普通の `Secret` を作る。
**Infisical 純正 operator**(`apps/infisical/operator.yaml`)が各リポジトリの `InfisicalSecret` を見て Infisical から値を取り、普通の `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 はここには無い**(`apps/infisical-operator/`、`apps/infisical-push-bridge/`)。
- **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`。
付けてある)。SA と RBAC は `apps/infisical/operator-rbac.yaml`。
operator 側に長期の資格情報は無い(`identityId` は秘密ではない)。

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

**GitHub Actions は読むだけ。** cilium-drift.yml の OIDC トークンを API サーバが直接受け
([../talos/patches/apiserver.yaml](../talos/patches/apiserver.yaml) の `KubeAuthenticationConfig`)、
([../talos/controlplane.yaml](../talos/controlplane.yaml) の `KubeAuthenticationConfig`)、
権限は `cilium-config` の `get` だけ([apiserver/rbac.yaml](apiserver/rbac.yaml))。
CA は [apiserver/ca.crt](apiserver/ca.crt)(`talos/secrets.yaml` のクラスタ CA。作り直したら替える)。
kubeconfig も age 鍵も GitHub に置いていない。
Expand All @@ -60,7 +60,7 @@ Headlamp が利用者本人の id_token を API サーバへ転送する。ア
`entra:admin` で cluster-admin、それ以外は `entra:viewer` で読み取り専用(組み込みの `view`
+ CRD とノードぶんの `readonly-extra`)。**振り分けは CEL で、割り当ては Entra 側で完結する。**
ここに置いたのは、ArgoCD も CI も要らずに効く層だから ── 締め出されたときに直せる。
認証設定([../talos/patches/apiserver.yaml](../talos/patches/apiserver.yaml))も [apiserver/rbac.yaml](apiserver/rbac.yaml) も
認証設定([../talos/controlplane.yaml](../talos/controlplane.yaml))も [apiserver/rbac.yaml](apiserver/rbac.yaml) も
machine config に載っているので、変えたら `render.sh` → `apply-config`(RBAC は inlineManifest なので `upgrade-k8s` まで)。
**ArgoCD には当てさせない**(`bootstrap` の exclude に `apiserver/`。締め出されたときに直せる層に置く)。

Expand Down Expand Up @@ -93,7 +93,7 @@ ArgoCD の tracking-id までコピーされると、ArgoCD がその `Secret`
`Prune=false` も一緒にコピーさせて、prune の対象から外す。

**`SkipDryRunOnMissingResource=true`** ── **まっさらなクラスタでは CRD がまだ無く、`apps/` の同期が丸ごと落ちる**
(CRD を入れる `apps/infisical-operator/` が同じ同期の中にいる)。
(CRD を入れる `apps/infisical/operator.yaml` が同じ同期の中にいる)。
仕組みは 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`)にも付けてある。
Expand Down
4 changes: 2 additions & 2 deletions bootstrap/apiserver/rbac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ roleRef:
kind: ClusterRole
name: cilium-drift
subjects:
# talos/patches/apiserver.yaml の claimMappings.username.expression が作る名前。
# talos/controlplane.yaml の claimMappings.username.expression が作る名前。
# `sub` は組織 ID / リポジトリ ID 入りの形に変わったので使っていない。
- apiGroup: rbac.authorization.k8s.io
kind: User
name: "gha:danything/gitops:refs/heads/main"
---
# ここから下は人。Entra の id_token を Headlamp が API サーバへ転送してくる
# (talos/patches/apiserver.yaml の 2 つめの issuer)。アプリロール admin を持つ人は
# (talos/controlplane.yaml の 2 つめの issuer)。アプリロール admin を持つ人は
# entra:admin、それ以外は entra:viewer のグループになって届く。
#
# CI と同じくこのファイルは machine config に載るので、クラスタを作り直した
Expand Down
Loading
Loading