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
27 changes: 4 additions & 23 deletions .github/workflows/talos-boot.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# bootstrap/render.sh の machine config を runner の QEMU(KVM)で起動し、クラスタが立つところまで見る。
# 秘密はゼロ(使い捨てとダミー)。外に副作用を出さない: ArgoCD の extraObjects と通知先、
# 本物の ClusterIssuer(自己署名に差し替え)、infisical の SMTP を抜く。手順の元は bootstrap/talos.md。
# 本物の ClusterIssuer(自己署名に差し替え)を抜く。手順の元は bootstrap/talos.md。
name: talos boot

on:
Expand Down Expand Up @@ -113,32 +113,17 @@ jobs:
yq -I4 'del(.sops) | del(.notifications.notifiers) | del(.githubAppRepoCreds.stringData.githubAppPrivateKey)
| .extraObjects = []' \
bootstrap/argocd-values.sops.yaml > /tmp/argocd-values.yaml
# infisical: 1 つめ(chart の値)はパスワードをダミーにして SMTP の env を抜く。2 つめ(Secret)は作り直す
# (ENCRYPTION_KEY は 16 バイトの hex でないと infisical が起動しない)。render.sh は `---` で分ける
{
yq -I4 'select(document_index == 0) | del(.sops)
| (.. | select(tag == "!!str" and test("^ENC\\["))) |= "ci-dummy"
| .infisical.extraEnv |= map(select(.name | test("^SMTP_") | not))' \
bootstrap/infisical-values.sops.yaml
echo ---
yq -I4 "select(document_index == 1) | del(.sops)
| del(.stringData.CLIENT_ID_GITHUB_LOGIN) | del(.stringData.CLIENT_SECRET_GITHUB_LOGIN)
| .stringData.ENCRYPTION_KEY = \"$(openssl rand -hex 16)\"
| .stringData.AUTH_SECRET = \"$(openssl rand -base64 32)\"
| .stringData.TELEMETRY_ENABLED = \"false\"" \
bootstrap/infisical-values.sops.yaml
} > /tmp/infisical-values.yaml
# cert-manager: 2 つめ(DNS-01 の Secret)のトークンをダミーに
yq -I4 'del(.sops) | (.. | select(tag == "!!str" and test("^ENC\\["))) |= "ci-dummy"' \
bootstrap/cert-manager-values.sops.yaml > /tmp/cert-manager-values.yaml
# sops-age: クラスタの age の鍵はダミー(operator は CI では復号しない。Secret の形だけ見る)
yq -I4 'del(.sops) | (.. | select(tag == "!!str" and test("^ENC\\["))) |= "AGE-SECRET-KEY-CI-DUMMY"' \
bootstrap/sops-age.sops.yaml > /tmp/sops-age.yaml
for f in /tmp/controlplane.yaml /tmp/argocd-values.yaml /tmp/infisical-values.yaml /tmp/cert-manager-values.yaml /tmp/sops-age.yaml; do
for f in /tmp/controlplane.yaml /tmp/argocd-values.yaml /tmp/cert-manager-values.yaml /tmp/sops-age.yaml; do
! grep -q 'ENC\[' "$f" || { echo "::error::$f に暗号文が残っている"; exit 1; }
done
SECRETS=/tmp/secrets.yaml CONTROLPLANE=/tmp/controlplane.yaml \
ARGOCD_VALUES=/tmp/argocd-values.yaml INFISICAL_VALUES=/tmp/infisical-values.yaml \
ARGOCD_VALUES=/tmp/argocd-values.yaml \
CERT_MANAGER_VALUES=/tmp/cert-manager-values.yaml SOPS_AGE=/tmp/sops-age.yaml \
./bootstrap/render.sh /tmp/out
talosctl config endpoint 127.0.0.1
Expand Down Expand Up @@ -240,9 +225,6 @@ jobs:
ro argocd deploy/argocd-server
ro argocd deploy/argocd-repo-server
ro argocd sts/argocd-application-controller
# postgres の PVC は既定の StorageClass(OpenEBS LVM の thin)なので、ここで PV の経路も見ている
ro infisical deploy/infisical
kubectl -n infisical wait --for=jsonpath='{.status.phase}'=Bound pvc --all --timeout=1m
# 上の一覧に無い Pod の取りこぼし止め
for i in $(seq 24); do
BAD=$(kubectl get pods -A --no-headers --field-selector=status.phase!=Running,status.phase!=Succeeded)
Expand Down Expand Up @@ -464,10 +446,9 @@ jobs:
until kubectl wait --for=condition=Ready "pod/$p" --timeout=10s >/dev/null 2>&1; do sleep 5; done
[ "$(kubectl exec "$p" -- cat /data/marker)" = hello ] || fail "再起動のあと $p の中身が無い"
done
kubectl -n infisical rollout status statefulset/postgresql --timeout=5m
kubectl -n kube-system rollout status ds/lvm-thinpool --timeout=3m
kubectl -n kube-system logs ds/lvm-thinpool -c pool
echo " 再起動のあとも t-src・t-dst・infisical の Postgres が上がった"
echo " 再起動のあとも t-src・t-dst が上がった"

- name: 失敗したときの様子
if: failure()
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tools.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ jobs:
- run: >-
docker run --rm tools sh -euc '
kubectl version --client; talosctl version --client --short; helm version --short;
sops --version; age --version; age-keygen --version; infisical --version; restic version;
sops --version; age --version; age-keygen --version; restic version;
pulumi version; bun --version; cf --version'
2 changes: 1 addition & 1 deletion .sops.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ creation_rules:
# 秘密を含む chart の値(と、2 つめのドキュメントの Secret)。秘密のキーだけ。キーを足したらここにも足す
# (当たらないキーは平文のままコミットされる)。
- path_regex: ^bootstrap/[^/]+-values\.sops\.yaml$
encrypted_regex: ^(service\.webhook\.mattermost|githubAppPrivateKey|password|postgresPassword|ENCRYPTION_KEY|AUTH_SECRET|CLIENT_ID_GITHUB_LOGIN|CLIENT_SECRET_GITHUB_LOGIN|CF_DNS_API_TOKEN)$
encrypted_regex: ^(service\.webhook\.mattermost|githubAppPrivateKey|CF_DNS_API_TOKEN)$
mac_only_encrypted: true
age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r
# クラスタの中で SopsSecret を復号する age の鍵(sops-secrets-operator が使う)。machine config に載せる
Expand Down
31 changes: 17 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,35 @@
| | |
| --- | --- |
| `deploy/` | Argo CD が再帰的に同期するアプリのマニフェスト。**`_` で始まるディレクトリは同期しない** (動かしていないものの置き場。下の「動かしていないもの」) |
| [`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 で起動して確かめる |
| [`bootstrap/`](bootstrap/) | クラスタそのものを組む層。**ここにあるものは全部 machine config に載る**([render.sh](bootstrap/render.sh) が描く)。Talos の設定([talos.sops.yaml](bootstrap/talos.sops.yaml))、Helm で入れるもの(Cilium・Argo CD 本体・cert-manager・OpenEBS LVM)、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)。手元にコマンドは入れない |
| [`tools/`](tools/) + `compose.yaml` | **手元の運用のコマンド(cf・sops・kubectl・talosctl など)はコンテナで動かす**。`tools/t.ps1 <コマンド>`(Windows の WSL コンテナ。wslc-compose)。手元にコマンドは入れない |
| [`docs/`](docs/) | [決定の記録](docs/decisions.md)、[Entra ID の認可](docs/entra.md)、[家のルーター](docs/router.md)、[Cloudflare の API トークン](docs/cloudflare-tokens.md) |
| [`ROADMAP.md`](ROADMAP.md) | やることと進捗(なぜそうしたかは `docs/decisions.md`) |
| `deploy/argocd.yaml` | このリポジトリ自身の Application 定義(他のリポジトリと同じ場所) |
| [`.sops.yaml`](.sops.yaml) | `bootstrap/*.sops.yaml` を SOPS(age)で暗号化する規則 |
| [`.sops.yaml`](.sops.yaml) | `*.sops.yaml` を SOPS(age)で暗号化する規則 |

## 秘密の扱い

**アプリの秘密は Infisical**(https://il.doany.io、このクラスタでセルフホスト)にあり、平文も暗号文も git には入らない。
各アプリの `*-secrets.yaml` は `InfisicalSecret` で、フォルダは `/<namespace>/<Secret 名>`、シークレット名が
そのまま Secret のキーになる。値を変えるのは Infisical の UI だけでよく、`secrets.infisical.com/auto-reload: "true"`
の注釈がある Deployment は Pod ごと入れ替わる。詳しくは [`bootstrap/README.md`](bootstrap/README.md)。
**秘密は全部 SOPS + age で暗号化して git に置く。** 平文は git に入らない。

**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` が復号して載せる)。書き方は [`bootstrap/README.md`](bootstrap/README.md)。
- **アプリの秘密**は各アプリの `deploy/secrets.sops.yaml`(このリポジトリでは `deploy/<アプリ>/secrets.sops.yaml`)の `SopsSecret`。
クラスタの中の [sops-secrets-operator](deploy/sops-secrets-operator/) が復号して `Secret` にし、
`reloader.stakater.com/auto: "true"` の注釈がある workload は [Reloader](deploy/reloader/) が入れ替える。
クラスタの鍵(`bootstrap/sops-age.sops.yaml`)と管理者の鍵の両方で暗号化する([`.sops.yaml`](.sops.yaml))。
- **クラスタを組む層の秘密**(`bootstrap/*.sops.yaml`。クラスタの CA、Argo CD が git を読む GitHub App 鍵、
cert-manager の Cloudflare トークン、fj.doany.io の資格情報、クラスタの age の鍵)は machine config に載る(`bootstrap/render.sh` が復号して載せる)。
書き方は [`bootstrap/README.md`](bootstrap/README.md)。

管理者の鍵は [`recovery/sops-age.key.age`](recovery/)(バックアップの `env.age` と同じパスフレーズ)。
2026-11-02 まではアプリの秘密をセルフホストの Infisical に置いていた(やめた理由は [docs/decisions.md](docs/decisions.md)「秘密は SOPS で git に置く」)。

```shell
# 鍵はリポジトリの .home/(git に入らない)に置く。コマンドは tools/t.ps1 で動かす(tools/README.md)
tools/t.ps1 sh -c 'mkdir -p ~/.config/sops/age && age -d -o ~/.config/sops/age/keys.txt recovery/sops-age.key.age'
tools/t.ps1 sops -d bootstrap/infisical-values.sops.yaml # 中を見る(画面に出すと平文になるので注意)
tools/t.ps1 sops edit deploy/matrix/secrets.sops.yaml # 直して push する(画面に出すと平文になるので -d で見るときは注意)
```

## ホスト名の付け方
Expand All @@ -53,7 +56,7 @@ tools/t.ps1 sops -d bootstrap/infisical-values.sops.yaml # 中を見る(画
| アプリ名 | 規則 | 結果 |
| --- | --- | --- |
| 2 語以上 | **それぞれの頭文字** | NetBird(Net + Bird)→ `nb`、Argo CD → `ac`、AdGuard Home → `ah` |
| 1 語 | **頭文字と最後の子音** | denpa → `dp`、hubble → `hl`、infisical → `il`、tamasagashi → `ts`、yosegaki → `yk`、proxy → `px` |
| 1 語 | **頭文字と最後の子音** | denpa → `dp`、hubble → `hl`、tamasagashi → `ts`、yosegaki → `yk`、proxy → `px` |

**1 文字で足りていたものはそのまま。** 先に取ったもの勝ちで、`a`(auth)・`d`(AdGuard)・
`h`(headlamp)・`l`(lgtm)・`w`(worklog)・`x`(xool)・`y`(yuzuriha)・`m`(matrix)・`e`(element)は 1 文字で置いてある。
Expand Down Expand Up @@ -82,7 +85,7 @@ headlamp が 1 文字なのは、規則どおりだと head + lamp でも hubble
| [`netbird/`](deploy/netbird/) | NetBird (VPN。combined コンテナ + 内蔵 IdP) |
| [`3proxy/`](deploy/3proxy/) | 3proxy (国内IP経由の HTTPS フォワードプロキシ) |
| [`volsync/`](deploy/volsync/)・[`backup/`](deploy/backup/) | バックアップ(VolSync で PVC のスナップショットを restic → Cloudflare R2)と、その見張り・etcd のスナップショット |
| [`infisical/`](deploy/infisical/) | 秘密の配布。本体は `bootstrap/` にあり、ここには公開経路と operator と即時反映のブリッジと SMTP のパスワード |
| [`sops-secrets-operator/`](deploy/sops-secrets-operator/)・[`reloader/`](deploy/reloader/) | 秘密の配布(git の `SopsSecret` を復号して Secret に)と、Secret が変わったときの Pod の入れ替え |
| [`cluster/`](deploy/cluster/) | metrics-server(Talos に無い。`kubectl top` と各 UI の使用量表示) |
| [`smtp-relay/`](deploy/smtp-relay/) | メールの中継。アプリの SMTP を受けて Microsoft 365 へ Graph で送る(Exchange Online の SMTP AUTH の Basic 認証の廃止に備えて)。**送る側の設定は [deploy/smtp-relay/README.md](deploy/smtp-relay/README.md)** |
| [`renovate/`](deploy/renovate/) | Renovate(GitHub の danything・5ym と Forgejo の doa の全リポジトリの依存を上げる PR)。CronJob で 15 分おき。**秘密の入れ方は [deploy/renovate/README.md](deploy/renovate/README.md)** |
Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Z440 は Intel AMT 9.x(ME 9.1)を持つ。OS が止まっていても**電源の

- **場所**: AMT は内蔵の I218-LM(`eno1`)にしか乗らないので、`eno1` を 10.10 側(`jcom0`)に回して 10.10.0.3 に置いた(#667)。作業用 PC から直接届く
- **設定(MEBx。起動中に Ctrl+P)**: IPv4 は固定で 10.10.0.3/24・ゲートウェイ 10.10.0.1、IPv6 は無効、SOL と IDE-R は有効、Network Access は Activate、
User Consent はなし、電源は「ON in S0, ME Wake in S3, S4-5」。パスワードは Infisical
User Consent はなし、電源は「ON in S0, ME Wake in S3, S4-5」。パスワードは Infisical に置いていた(2026-11-02 にやめた)
- **守り**: パスワード 1 つで電源もコンソールも握れるので**家の外に出さない**(DMZ と IPv6 の公開は 10.0.0.2 と bond0 の MAC だけなので当たらない)。
口は 16992・16993(管理)と 16994・16995(SOL・IDE-R)。ノード自身からは自分の AMT に届かない
- **つなぎ方**: Windows は Intel の [Device Management Toolkit の Console](https://github.com/device-management-toolkit/console)(`console_windows_x64.exe`。常駐させない)。
Expand Down
Loading
Loading