Webhook icon
Schedule icon
WorkingDirectory icon
Clone icon
Script icon
Docker icon
If icon
SlackIncomingWebhook icon
Log icon
Pause icon
Fail icon
Commands icon
Loop icon
Command icon
Set icon

SSH Authorized Keys Policy Drift Enforcement Gate

Compare authorized_keys on Linux hosts with a Git policy by fingerprint, remove drift after approval with backups, and verify each host over SSH.

Categories
Infrastructure

Keys pile up in authorized_keys: contractors who left, test keys, a laptop key copied "just for today". This blueprint treats a Git-held policy as the truth, ages out-of-policy keys, and removes them safely instead of just alerting.

How it works

  1. A webhook (from a Git push on the policy repo) or a four-hourly schedule starts the flow; concurrency allows one run at a time.
  2. clone_policy (io.kestra.plugin.git.Clone) fetches the policy: groups of hosts, users and allowed key fingerprints with optional owner and expiry.
  3. scan_hosts connects to each host with ssh2, reads every user's authorized_keys and computes OpenSSH SHA256 fingerprints, handling option-prefixed lines.
  4. A key not in the policy enters a KV ledger with its first-seen time and becomes removable after grace_hours; an expired key is removable at once. The key Kestra connects with is never removed, weak allowed keys are only reported, and a host where too many keys look wrong is blocked.
  5. pause_for_approval holds the plan until an operator resumes it and types the number of keys.
  6. recheck_policy clones the policy again and stops if its checksum differs from the one the plan was built on, so a key granted while the plan waited is not removed.
  7. remediate_hosts loops over the hosts with io.kestra.plugin.fs.ssh.Command, copying each file to authorized_keys.kestra-bak-<execution> and deleting only the recorded lines.
  8. verify_removal reads every host again: removed fingerprints gone, retained ones present, backups exist.

What you get

  • Drift removal with a rollback file on every host instead of an alert nobody acts on.
  • Protection against lockout, empty policies and policy typos.
  • A verified audit entry in KV with the approver note.

Who it's for

  • Platform and security teams that manage SSH access to Linux fleets.

Why orchestrate this with Kestra

The grace ledger needs memory, the removal needs a human decision and the proof needs an independent second read. Git, SSH, KV, Pause and Loop are one declarative flow.

Prerequisites

  • Linux hosts reachable over SSH with passwordless sudo for the Kestra account, Docker on the worker and a Slack webhook.
  • The SHA256 host key fingerprint of every policy host in the policy's host_keys. The scan and the verification refuse to talk to a host that does not match. The edit step uses the Kestra SSH task, which does not check host keys by default, so the verification read is what proves the result.

Secrets

  • SSH_PRIVATE_KEY: unencrypted private key of the Kestra account.
  • SLACK_WEBHOOK_URL: Slack Incoming Webhook.
  • SSH_DRIFT_WEBHOOK_KEY: key protecting the webhook trigger.

Quick start

  1. Start a test host with docker run -d -p 2222:2222 -e PUBLIC_KEY="$(cat kestra.pub)" -e USER_NAME=kestra -e SUDO_ACCESS=true linuxserver/openssh-server, set ssh_port to 2222 and list host.docker.internal as the policy host.
  2. Commit an access-policy.json listing the host, the users, their allowed fingerprints (ssh-keygen -lf key.pub) and host_keys with the host's fingerprint (ssh-keyscan -t ed25519 HOST | ssh-keygen -lf -).
  3. Create the three secrets, add an unlisted key to the host and execute with dry_run: true.
  4. Execute with dry_run: false and grace_hours: 0, resume the pause with the planned count and check the verified message.

How to extend

  • Restore from authorized_keys.kestra-bak-<execution> from a rollback flow.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.