orchestration.md: fix the Kestra identity claim, add live evidence

The old claim described two flows sharing one keypair under different
usernames as a deliberate scoping decision. That was actually a stale,
half-migrated credential mismatch, not a design choice - the real fix was
giving Kestra a dedicated keypair with zero relation to the ansible
automation identity's own key, so a leak of one can never expose the other.
Kestra's SSH plugin has no certificate support at all (a plain Java client,
confirmed via its own plugin schema), which is the actual reason it needs
its own static key regardless of what the fleet's ansible identity does.

Added the real current flow config (kestra_local_user/kestra_local_key) and
a screenshot of the live KV Store showing exactly those two entries. Also
refreshed the automation-self-update.timer example, which had gone stale.
This commit is contained in:
automation-bot committed 2026-09-05 08:31:54 +03:00
1 parent d4f538f5e0
commit 9fd60f3f3b
2 files changed
+17 -3

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

+17 -3
View File
@@ -37,10 +37,24 @@ disk-cleanup: 0
app-update: 1
```
## Identity Split, Per Flow
## One Dedicated Identity, Not a Shared One
> [!IMPORTANT]
> Most flows connect using one operator identity over SSH. One flow — the one that needs to run Ansible itself on the control node, not just reach out over SSH to a target — deliberately logs in as a separate, narrower service identity instead, since it needs local execution rights on the control node that the SSH-only identity doesn't have. Same underlying key material either way, just presented as two different accounts depending on what the flow actually needs to do locally, following the same "scope credentials to exactly what's needed" instinct as the least-privilege Git access model in [`docs/sso-and-identity.md`](sso-and-identity.md). Worth checking which one a given flow uses before assuming — it's not always the same answer, and assuming wrong is a fast way to get a confusing permission error mid-debugging.
> Every flow connects to automation using the exact same credential — a dedicated keypair that exists for this one purpose and nothing else, unrelated to the `ansible` automation identity used everywhere else in this repo. That separation is deliberate, not incidental: Kestra's SSH task plugin is a plain Java SSH client with no certificate support at all, so it can only ever authenticate with a static key — it has no way to participate in the certificate-based model the rest of the fleet's `ansible` identity uses (see [`docs/secrets-and-pki.md`](secrets-and-pki.md)). Giving Kestra its own key instead of reusing `ansible`'s means a leak of one credential can never expose the other — the blast radius of "Kestra's automation-host hop" and "the fleet-wide `ansible` identity" stay fully independent. This is the real config, not a paraphrase — every SSH task in every flow points at the same two KV entries:
>
> ```
> $ curl -s .../flows/homelab/app-update?source=true | grep -A2 'type: io.kestra.plugin.fs.ssh.Command'
> type: io.kestra.plugin.fs.ssh.Command
> ...
> username: "{{ kv('kestra_local_user') }}"
> privateKey: "{{ kv('kestra_local_key') }}"
> ```
>
> The SSH hop itself is only ever a means to escape Kestra's own container and reach the control node's local filesystem and tooling — it's never used to reach the wider fleet. `ansible-playbook`, once it's actually running (as a local process, invoked over that same SSH hop), makes its own outbound connections to every other fleet host, using the fleet-wide `ansible` identity and its own certificate.
This is the actual KV Store, not a description of it — exactly two entries, nothing else:
![Kestra's homelab-namespace KV Store: exactly two entries, kestra_local_key and kestra_local_user, the dedicated credential every flow's SSH task uses](../assets/orchestration-kestra-kv-store.png)
## A Real Special Case, Thought Through In Advance
@@ -76,7 +90,7 @@ The automation host's own OS patches and Kestra's own container updates run via
```
$ systemctl list-timers automation-self-update.timer
NEXT LEFT LAST PASSED UNIT
Sat 2026-08-29 04:00:00 EEST ... Sat 2026-08-22 04:00:26 EEST - automation-self-update.timer
Sat 2026-09-12 04:00:00 EEST 6 days Sat 2026-09-05 04:00:32 EEST 4h 17min automation-self-update.timer
```
---