security: pin SSH host key instead of StrictHostKeyChecking=no in Deployer #161

Closed
opened 2026-05-23 08:55:31 +00:00 by guettlibot · 15 comments
guettlibot commented 2026-05-23 08:55:31 +00:00 (Migrated from codeberg.org)

Problem

All SSH and rsync operations in the deploy pipeline use `-o StrictHostKeyChecking=no`:

  • `Deployer()` (ci/main.go ~line 325): sets `RSYNC_RSH` env var with `StrictHostKeyChecking=no`
  • `DeployApk` and `DeployLinux` (lines ~603, 604, 642, 643): explicit `-o StrictHostKeyChecking=no` on `ssh` and `scp` calls

This disables host key verification entirely. A network-level attacker on the same segment as the CI runner (ARP spoofing, DNS poisoning of `SSH_HOST`) could intercept the connection and receive deployed artifacts or SSH auth challenges.

Fix

  1. Run `ssh-keyscan ` on a trusted network and store the output as a new Codeberg secret, e.g. `SSH_KNOWN_HOSTS`.

  2. *Add a `knownHosts dagger.Secret` parameter to `Deployer()` and mount it:

```go
func (m *Ci) Deployer(sshKey *dagger.Secret, knownHosts *dagger.Secret) *dagger.Container {
return dag.Container().
From("alpine:3.21").
WithExec([]string{"apk", "--no-cache", "add", "rsync", "openssh-client", "python3", "tar"}).
WithMountedSecret("/root/.ssh/id_ed25519", sshKey, dagger.ContainerWithMountedSecretOpts{Mode: 0600}).
WithMountedSecret("/root/.ssh/known_hosts", knownHosts, dagger.ContainerWithMountedSecretOpts{Mode: 0644}).
WithEnvVariable("RSYNC_RSH", "ssh -i /root/.ssh/id_ed25519") // no StrictHostKeyChecking=no
}
```

  1. Remove all `-o StrictHostKeyChecking=no` from individual `ssh`/`scp` calls.

  2. Pass `SSH_KNOWN_HOSTS` as a secret through the Taskfile and workflow YAML.

Note

`ssh-keyscan` itself is TOFU (Trust On First Use) — run it manually on a machine you already trust to have a legitimate route to the server, capture the output, and store it as the secret. Rotate it if the server's host key changes.

## Problem All SSH and rsync operations in the deploy pipeline use \`-o StrictHostKeyChecking=no\`: - \`Deployer()\` (ci/main.go ~line 325): sets \`RSYNC_RSH\` env var with \`StrictHostKeyChecking=no\` - \`DeployApk\` and \`DeployLinux\` (lines ~603, 604, 642, 643): explicit \`-o StrictHostKeyChecking=no\` on \`ssh\` and \`scp\` calls This disables host key verification entirely. A network-level attacker on the same segment as the CI runner (ARP spoofing, DNS poisoning of \`SSH_HOST\`) could intercept the connection and receive deployed artifacts or SSH auth challenges. ## Fix 1. **Run \`ssh-keyscan <deploy-host>\` on a trusted network** and store the output as a new Codeberg secret, e.g. \`SSH_KNOWN_HOSTS\`. 2. **Add a \`knownHosts *dagger.Secret\` parameter to \`Deployer()\`** and mount it: \`\`\`go func (m *Ci) Deployer(sshKey *dagger.Secret, knownHosts *dagger.Secret) *dagger.Container { return dag.Container(). From("alpine:3.21"). WithExec([]string{"apk", "--no-cache", "add", "rsync", "openssh-client", "python3", "tar"}). WithMountedSecret("/root/.ssh/id_ed25519", sshKey, dagger.ContainerWithMountedSecretOpts{Mode: 0600}). WithMountedSecret("/root/.ssh/known_hosts", knownHosts, dagger.ContainerWithMountedSecretOpts{Mode: 0644}). WithEnvVariable("RSYNC_RSH", "ssh -i /root/.ssh/id_ed25519") // no StrictHostKeyChecking=no } \`\`\` 3. **Remove all \`-o StrictHostKeyChecking=no\`** from individual \`ssh\`/\`scp\` calls. 4. **Pass \`SSH_KNOWN_HOSTS\` as a secret** through the Taskfile and workflow YAML. ## Note \`ssh-keyscan\` itself is TOFU (Trust On First Use) — run it manually on a machine you already trust to have a legitimate route to the server, capture the output, and store it as the secret. Rotate it if the server's host key changes.
guettlibot commented 2026-05-23 15:55:07 +00:00 (Migrated from codeberg.org)

Agent opened PR #181 but no CI run appeared on branch issue-161-fix after 74 min. The agent may not have pushed any commits. Please investigate and resume manually.

Agent opened PR #181 but no CI run appeared on branch `issue-161-fix` after 74 min. The agent may not have pushed any commits. Please investigate and resume manually.
guettlibot commented 2026-05-24 08:50:13 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 09:10:17 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 09:15:15 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 09:20:19 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 09:25:12 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 09:30:24 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 09:35:13 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 09:40:19 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 09:45:20 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 10:00:35 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 10:10:18 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 10:15:14 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 10:20:14 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
guettlibot commented 2026-05-24 10:25:16 +00:00 (Migrated from codeberg.org)

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.

Automatic merge of PR #181 failed (PR is still open after the merge command). Please merge manually.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: guettli/sharedinbox#161