Skip to main content

128. Bootstrap SSH Key Strategy

Status: Accepted Date: 2025-07-06

Context

When provisioning a new server, we face a classic "chicken-and-egg" problem with SSH keys. To run our Ansible automation, we need to be able to SSH into the server. But to SSH into the server, we need our SSH keys to be present in the ~/.ssh/authorized_keys file.

Furthermore, we manage our complete set of personal SSH keys, configs, and identities through our dotfiles repository, deployed via chezmoi (adr://chezmoi-integration). We don't want to duplicate all these private keys inside our Ansible vault.

Decision

We will use a two-phase bootstrap strategy for SSH keys.

Phase 1: Ansible Bootstrap

  • A single, dedicated public SSH key, which we'll call the "bootstrap key", will be stored as a variable within the Ansible project.
  • The very first task in our common Ansible roles (02_ssh) will be to ensure this single bootstrap public key is present in the user's authorized_keys file.
  • This key is used only for the initial connection and execution of the Ansible playbook.

Phase 2: Dotfile Deployment

  • A later Ansible role (27_chezmoi) is responsible for installing chezmoi and running it to deploy our full dotfiles configuration.
  • Our dotfiles repository contains the complete ~/.ssh directory, including the authorized_keys file with all our personal and work keys, as well as our SSH config.
  • When chezmoi runs, it will overwrite the authorized_keys file with the complete version from our dotfiles.

This strategy ensures that Ansible has a minimal, dedicated key to get started, but the true source of truth for our SSH configuration remains our secure, personal dotfiles repository.

Consequences

Positive:

  • Solves the Chicken-and-Egg Problem: Provides a clean, reliable solution for initial server access.
  • Secure: We are not storing our sensitive, primary private keys inside the Ansible project. Only a single, purpose-specific public key is stored there. If the bootstrap key is ever compromised, we can simply replace it without having to rotate all our personal keys.
  • Single Source of Truth: The user's dotfiles repository remains the single source of truth for their SSH identity, which is the correct separation of concerns. The infrastructure code provisions the system, the dotfiles provision the user's environment.

Negative:

  • Brief Window of Limited Access: There is a brief period during the initial provisioning where only the bootstrap key will grant access.
  • Dependency on chezmoi: This strategy is dependent on the chezmoi role running successfully to establish full, normal SSH access.

Mitigation:

  • Automated Process: The window of limited access is very short, as it only exists for the duration of the Ansible run. Since the process is fully automated, this is not a practical issue.
  • Critical Role Ordering: The chezmoi role is one of the last and most critical roles to run. The Ansible playbook is designed to be idempotent. If the chezmoi role fails, the playbook can simply be re-run until it succeeds, at which point full access will be established.