How actions/checkout Handles SSH Known Hosts and Strict Host Key Checking

When you provide an SSH deploy key, actions/checkout constructs a temporary known-hosts file and exports a custom GIT_SSH_COMMAND that enforces strict host-key verification when the ssh-strict input is enabled.

The actions/checkout GitHub Action manages SSH authentication securely by generating isolated configuration files rather than modifying the runner's global SSH settings. When using the ssh-key input, the action implements a multi-step process in src/git-auth-helper.ts to handle known hosts and strict host-key checking without persisting sensitive data to the runner's default ~/.ssh directory.

SSH Authentication Architecture

The Known Hosts File Construction

In src/git-auth-helper.ts, the configureSsh() function creates a temporary known-hosts file that consolidates three distinct sources:

  1. The runner's existing ~/.ssh/known_hosts file (if present)
  2. Additional host entries provided via the ssh-known-hosts input
  3. An implicit entry for github.com that ships with the action

The concatenated content is written to a unique path under $RUNNER_TEMP (e.g., $RUNNER_TEMP/a1b2c3_known_hosts). This path is immediately registered via stateHelper.setSshKnownHostsPath() to ensure the file can be located and deleted during the post-action cleanup phase.

Building the GIT_SSH_COMMAND

The action constructs a custom SSH command string that enforces specific security policies. It locates the system SSH binary using io.which('ssh'), then builds the command with the following components:

  • -i "$RUNNER_TEMP/<key-file>": Points to the temporary private key file
  • -o "UserKnownHostsFile=$RUNNER_TEMP/<known-hosts-file>": Isolates host verification to the temporary file
  • Strict host-key checking: When ssh-strict is true, the action appends -o StrictHostKeyChecking=yes -o CheckHostIP=no as documented in adrs/0153-checkout-v2.md

This complete command string is exported as the GIT_SSH_COMMAND environment variable, ensuring all subsequent Git operations (git clone, git fetch, submodule updates) use these exact SSH settings.

Configuration Inputs and Workflow Usage

The action.yml file defines three inputs that control SSH behavior:

  • ssh-key: The private SSH key for authentication
  • ssh-known-hosts: Additional known hosts entries in standard SSH format
  • ssh-strict: Boolean flag to enable strict host-key checking

When persist-credentials: true, the action also writes the SSH command to the repository's local Git config under core.sshCommand. This ensures submodule operations inherit the same authentication settings as the parent repository.

Example workflow configuration:

steps:
  - uses: actions/checkout@v4
    with:
      ssh-key: ${{ secrets.DEPLOY_SSH_KEY }}
      ssh-known-hosts: |
        my.custom.host ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...
      ssh-strict: true

This generates an environment variable similar to:

GIT_SSH_COMMAND="ssh -i \"$RUNNER_TEMP/8f2c3a\" \
  -o StrictHostKeyChecking=yes -o CheckHostIP=no \
  -o \"UserKnownHostsFile=$RUNNER_TEMP/8f2c3a_known_hosts\""

Cleanup and Security

The action implements secure cleanup through the removeAuth() function in src/git-auth-helper.ts, which invokes removeSsh() to delete both the temporary private key and known-hosts files from $RUNNER_TEMP. It also clears the core.sshCommand Git configuration to prevent credential leakage to subsequent workflow steps.

The combination of StrictHostKeyChecking=yes with CheckHostIP=no ensures that host keys are verified against the provided known-hosts file while avoiding IP-based verification that could fail in dynamic cloud environments.

Summary

  • Temporary known-hosts file: Concatenates system, user-provided, and built-in GitHub entries into an isolated file under $RUNNER_TEMP.
  • Strict host-key checking: Enabled via ssh-strict: true, adding -o StrictHostKeyChecking=yes to the SSH command as implemented in src/git-auth-helper.ts.
  • GIT_SSH_COMMAND: Exported environment variable ensures all Git operations use the custom key and verification settings.
  • Automatic cleanup: Temporary files and Git configurations are removed via stateHelper when the action completes.

Frequently Asked Questions

Does actions/checkout verify GitHub's host key by default?

Yes. The action includes a built-in known-hosts entry for github.com that is automatically appended to the temporary known-hosts file, ensuring verification occurs even if the runner's global ~/.ssh/known_hosts does not contain GitHub's key.

What happens when ssh-strict is set to false?

When ssh-strict is disabled, the action omits the -o StrictHostKeyChecking=yes flag from the SSH command. This allows connections to proceed without strict verification, though the UserKnownHostsFile option still points to the temporary file containing any explicitly provided host keys.

How does the action handle SSH keys for submodules?

When persist-credentials: true, the action writes the SSH command to the repository's Git config under core.sshCommand. This configuration persists for submodule operations, ensuring they use the same SSH key and known-hosts settings as the parent repository.

Where are the temporary SSH files stored?

The action stores temporary files in the directory specified by $RUNNER_TEMP, generating unique filenames for both the private key and known-hosts file. These paths are tracked via stateHelper.setSshKnownHostsPath() and stateHelper.setSshKeyPath() to ensure reliable cleanup during the post-action phase.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →