diff --git a/.gitignore b/.gitignore index c78a3db..6db9d05 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ talos/secrets.plain.yaml # **平文のまま置かないこと。** talos/registries.yaml は SOPS 済みのものをコミットする # (talos/README.md「fj.doany.io の資格情報」)。書きかけの平文はこの名前に逃がす。 talos/registries.plain.yaml -# 平文の kubeconfig / talosconfig も置かない。talos/kubeconfig.yaml は SOPS 済みのものをコミットする +# 平文の kubeconfig / talosconfig も置かない。どちらも tools/t.ps1 が毎回その場で作る # (tools/README.md「クラスタの資格情報」)。talosctl kubeconfig と gen config がこの名前で書き出す kubeconfig talosconfig diff --git a/.sops.yaml b/.sops.yaml index a36835b..e985698 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -18,8 +18,3 @@ creation_rules: # 選り分ける意味が無い(k3s 期の /etc/rancher/k3s/registries.yaml の移し先)。 - path_regex: ^talos/registries\.yaml$ age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r - # talos/kubeconfig.yaml(管理者。`talosctl kubeconfig` の出力)も丸ごと。 - # 中身は証明書と鍵がほとんどで、選り分ける意味が無い。tools/t.ps1 の kubectl がその場で復号して使う - # (tools/README.md「クラスタの資格情報」)。 - - path_regex: ^talos/kubeconfig\.yaml$ - age: age1sgnnkn67wpt4wgjsff4c36w3cmjkk557ng504e4aczx2lzls9y6ssy0x9r diff --git a/talos/README.md b/talos/README.md index b79dae0..0bdc34c 100644 --- a/talos/README.md +++ b/talos/README.md @@ -45,8 +45,8 @@ talosctl -n 10.0.0.2 -e 10.0.0.2 --talosconfig /tmp/talos-config/talosconfig boo talosctl -n 10.0.0.2 -e 10.0.0.2 --talosconfig /tmp/talos-config/talosconfig kubeconfig ``` -ふだんの kubectl / talosctl は `tools/t.ps1` が資格情報を用意する。管理者の kubeconfig は SOPS で -`talos/kubeconfig.yaml` に置く(手順は [../tools/README.md](../tools/README.md)「クラスタの資格情報」)。 +ふだんの kubectl / talosctl は `tools/t.ps1` が資格情報を毎回その場で作る。管理者の kubeconfig も +ファイルでは持たない([../tools/README.md](../tools/README.md)「クラスタの資格情報」)。 `clusterconfig/` と平文の秘密は `.gitignore` 済み。 diff --git a/tools/README.md b/tools/README.md index e680012..3bd24c2 100644 --- a/tools/README.md +++ b/tools/README.md @@ -57,31 +57,21 @@ tools/t.ps1 # 引数なしならシェルに入る | | 読み取り用(既定) | 管理者(`--admin`) | | --- | --- | --- | -| kubectl / helm | その場で作る。`talos/secrets.yaml` の Kubernetes の CA で、グループ `entra:viewer` の証明書(1 日で切れる) | `talos/kubeconfig.yaml`(SOPS 済み)を復号する | +| kubectl / helm | その場で作る。`talos/secrets.yaml` の Kubernetes の CA で、グループ `entra:viewer` の証明書(1 日で切れる) | 同じく `system:masters` | | talosctl | その場で作る。Talos の CA で `os:reader` の証明書(1 日で切れる) | 同じく `os:admin` | - 読み取り用の権限は [`../bootstrap/apiserver/rbac.yaml`](../bootstrap/apiserver/rbac.yaml) の `entra:viewer` (組み込みの `view` と `readonly-extra`。ログは読めて、Secret は読めない)。Headlamp で Entra の viewer が持つものと同じ - **helm はリリースの情報を Secret に持つ**ので、`helm list` や `helm status` も `--admin` を付ける - **talosconfig は保存しない**([../docs/decisions.md](../docs/decisions.md)「手で持つもの」)。`secrets.yaml` から毎回作る -- 管理者の kubeconfig だけはファイルで持つ。Talos が出す正式なもの(`talosctl kubeconfig`)を使い、証明書は 1 年で切れる。 - 中身は `secrets.yaml` の CA で作れるものなので、置いても開けられる範囲は今と変わらない +- **管理者の kubeconfig も保存しない。** 毎回その場で作るので、作り直しも期限切れも無い。 + `talosctl kubeconfig` は 1 年もつ証明書を呼ぶたびに出す(取り消せない)ので使わない - 行き先はどれも `10.10.0.4`(ノードの、作業用 PC の LAN 側の口。`creds.sh` の `ep`)。`talos/render.sh` の `10.0.0.2` は クラスタの LAN で手元から届かず、NetBird はクラスタの上で動くので落ちたときに使えない。API は外に開かない。 外からは mado で作業用 PC に入って打つ。`10.10.0.4` は Talos の証明書の SAN に入っている。`KUBECONFIG` / `TALOSCONFIG` や `--kubeconfig` / `--talosconfig` / `--server` を自分で渡したときは何もしない。繋がないもの(`kubectl kustomize`、`helm template`、`talosctl gen`、`--client` など)も同じ -**管理者の kubeconfig の入れ方**(作り直すときも同じ。切れる前に): - -```powershell -tools/t.ps1 --admin sh -c 'talosctl kubeconfig /dev/shm/k && sops -e --filename-override talos/kubeconfig.yaml /dev/shm/k > talos/kubeconfig.yaml' -tools/t.ps1 --admin kubectl get nodes # 確かめる -git add talos/kubeconfig.yaml # 暗号化済み(`sops:` がある)ことを見てからコミットする -``` - -`talosctl kubeconfig` が書き出す先も暗号化の入力も `/dev/shm` なので、平文は一度もディスクに出ない。 -規則は [`../.sops.yaml`](../.sops.yaml)。平文の `kubeconfig` / `talosconfig` という名前のファイルは `.gitignore` 済みで、 -`creds.sh` も暗号化されていない `talos/kubeconfig.yaml` は使わずに止まる。 +平文の `kubeconfig` / `talosconfig` という名前のファイルは `.gitignore` 済み(手で書き出したものを誤って入れないように)。 ## wslc の制限 diff --git a/tools/creds.sh b/tools/creds.sh index f7abcab..bbf607c 100755 --- a/tools/creds.sh +++ b/tools/creds.sh @@ -5,7 +5,7 @@ # # 既定は読み取り用。管理者は `tools/t.ps1 --admin …`(GITOPS_ADMIN=1)のときだけ。 # kubectl / helm 読み取り用は talos/secrets.yaml の CA でその場で証明書を作る(1 日で切れる)。 -# 管理者は talos/kubeconfig.yaml(SOPS 済み。talosctl kubeconfig の出力)を復号する +# 管理者も同じく system:masters の証明書をその場で作る(ファイルで持たない) # talosctl 同じく CA でその場で作る(os:reader / os:admin)。talosconfig は保存しない # (docs/decisions.md「手で持つもの」) set -eu @@ -88,35 +88,41 @@ mint() { # render.sh の 10.0.0.2 はクラスタの LAN で、手元からは届かない。NetBird はクラスタの上で動くので、 # クラスタが落ちたときに使えない。10.10.0.4 は Talos の証明書(apid・kube-apiserver)の SAN に入っている ep=10.10.0.4 -if [ "$tool" = talosctl ]; then - out=$shm/talosconfig-$role - if [ ! -s "$out" ]; then - if [ $role = admin ]; then os=os:admin; else os=os:reader; fi - mint os "/O=$os" "$out.c.$$" ed25519 + +# talosconfig を $shm に作って置き場所を返す($1 = admin / readonly)。作るのは初回だけ +talosconfig() { + t=$shm/talosconfig-$1 + if [ ! -s "$t" ]; then + if [ "$1" = admin ]; then os=os:admin; else os=os:reader; fi + mint os "/O=$os" "$t.c.$$" ed25519 (umask 077 && printf 'context: doany\ncontexts:\n doany:\n endpoints: [%s]\n nodes: [%s]\n ca: %s\n crt: %s\n key: %s\n' \ - "$ep" "$ep" "$(cat "$out.c.$$.ca")" "$(cat "$out.c.$$.crt")" "$(cat "$out.c.$$.key")" >"$out.$$" && mv "$out.$$" "$out") - rm -f "$out.c.$$".* + "$ep" "$ep" "$(cat "$t.c.$$.ca")" "$(cat "$t.c.$$.crt")" "$(cat "$t.c.$$.key")" >"$t.$$" && mv "$t.$$" "$t") + rm -f "$t.c.$$".* fi - export TALOSCONFIG="$out" + echo "$t" +} + +if [ "$tool" = talosctl ]; then + TALOSCONFIG=$(talosconfig $role) + export TALOSCONFIG exec "$real" "$@" fi out=$shm/kubeconfig-$role -if [ ! -s "$out" ] && [ $role = readonly ]; then - # 読み取り用は bootstrap/apiserver/rbac.yaml の entra:viewer(組み込みの view + readonly-extra。Secret は読めない)。 - # Headlamp で Entra の viewer が持つ権限と同じにするため、同じグループ名を使う - mint k8s "/O=entra:viewer/CN=tools-readonly" "$out.c.$$" ec - (umask 077 && printf 'apiVersion: v1\nkind: Config\nclusters:\n- name: doany\n cluster:\n server: https://%s:6443\n certificate-authority-data: %s\ncontexts:\n- name: readonly@doany\n context:\n cluster: doany\n user: readonly\ncurrent-context: readonly@doany\nusers:\n- name: readonly\n user:\n client-certificate-data: %s\n client-key-data: %s\n' \ - "$ep" "$(cat "$out.c.$$.ca")" "$(cat "$out.c.$$.crt")" "$(cat "$out.c.$$.key")" >"$out.$$" && mv "$out.$$" "$out") +if [ ! -s "$out" ]; then + if [ $role = admin ]; then + # 管理者は system:masters(Talos の talosctl kubeconfig と同じグループ)。talosctl kubeconfig は 1 年もつ + # 証明書を毎回出すので使わず、読み取り用と同じく 1 日で切れるものを作る + subj=/O=system:masters/CN=tools-admin + else + # 読み取り用は bootstrap/apiserver/rbac.yaml の entra:viewer(組み込みの view + readonly-extra。Secret は読めない)。 + # Headlamp で Entra の viewer が持つ権限と同じにするため、同じグループ名を使う + subj=/O=entra:viewer/CN=tools-readonly + fi + mint k8s "$subj" "$out.c.$$" ec + (umask 077 && printf 'apiVersion: v1\nkind: Config\nclusters:\n- name: doany\n cluster:\n server: https://%s:6443\n certificate-authority-data: %s\ncontexts:\n- name: %s@doany\n context:\n cluster: doany\n user: %s\ncurrent-context: %s@doany\nusers:\n- name: %s\n user:\n client-certificate-data: %s\n client-key-data: %s\n' \ + "$ep" "$(cat "$out.c.$$.ca")" $role $role $role $role "$(cat "$out.c.$$.crt")" "$(cat "$out.c.$$.key")" >"$out.$$" && mv "$out.$$" "$out") rm -f "$out.c.$$".* -elif [ ! -s "$out" ]; then - f=talos/kubeconfig.yaml - [ -f $f ] || die "$f が無い。作り方は tools/README.md「クラスタの資格情報」" - grep -q '^sops:' $f || die "$f が暗号化されていない。git に入れる前に tools/README.md の手順で暗号化する" - nokey - # talosctl kubeconfig が書く行き先は 10.0.0.2(クラスタの endpoint)なので、手元から届く口に替える - (umask 077 && sops -d $f >"$out.$$" && sed -i "s#^\( *server:\) https://.*#\1 https://$ep:6443#" "$out.$$" && mv "$out.$$" "$out") || - { rm -f "$out.$$"; die "$f を復号できなかった"; } fi export KUBECONFIG="$out" exec "$real" "$@"