A compact GitHub/Gitea Action to start an SSH agent, load one or more private keys (volatile — not persisted to disk), and optionally rewrite HTTPS submodule URLs to SSH for CI jobs.
This action exists primarily to make working with private Git submodules easy and safe in CI. It avoids changing global Git configuration on the runner (for example, using a command like:
git config --global url."git@github.com:".insteadOf "https://github.com/") because global changes persist on a runner and can interfere with parallel jobs or subsequent workflow runs on personal/self-hosted runners. Preventing that runner-wide side effect is the main design goal of this repository.
Use the auto-cleanup action (recommended) for most cases:
- name: Setup SSH for submodules
uses: zcube/submodule-init@main
with:
https-host: 'git.example.com' # HTTPS host to match
ssh-host: 'nas.example.com:2222' # SSH host[:port]
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Checkout with submodules
uses: actions/checkout@v4
with:
submodules: recursiveIf you only need a generic SSH agent (no URL rewriting):
- name: Setup SSH agent
uses: zcube/submodule-init@main
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Use SSH
run: git clone git@github.com:user/repo.gitFor explicit (manual) cleanup control use the manual-cleanup action and the cleanup-action:
- name: Setup SSH (manual cleanup)
id: ssh
uses: zcube/submodule-init/manual-cleanup-action.yml@main
with:
https-host: 'git.example.com'
ssh-host: 'nas.example.com:2222'
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Cleanup SSH agent
if: always()
uses: zcube/submodule-init/cleanup-action.yml@main
with:
ssh-agent-pid: ${{ steps.ssh.outputs.ssh-agent-pid }}- URL rewriting (single): provide
https-host+ssh-host. - URL rewriting (multiple): use
url-mappingsfor multiple host mappings. - Generic SSH agent: omit
https-hostandssh-hostto only load keys.
| Name | Description | Required | Default | Example |
|---|---|---|---|---|
ssh-private-key |
Private key(s); multiple keys separated by newlines. | Yes | - | ---BEGIN RSA... (paste key) |
https-host |
HTTPS hostname to match (used when rewriting). | No | - | github.com |
ssh-host |
SSH hostname (optionally with :port). | No | - | github.com:22 or nas.example.com:2222 |
ssh-port |
SSH port used if ssh-host has no port. |
No | 22 |
22 |
url-mappings |
Multi-line mappings. Format: https://<https-host>/=ssh://<user>@<ssh-host>[:port]/ (one per line). Example below. |
No | - | https://github.com/=ssh://git@github.com:22/ |
ssh-known-hosts |
Known hosts entries (recommended for production). | No | - | github.com ssh-rsa AAAA... (see Creating secrets) |
strict-host-key-checking |
'yes' or 'no' (enable host key checking). | No | yes |
yes |
| Topic | Notes |
|---|---|
| Secrets | Provide keys only via repository or organization secrets. |
| Key persistence | Keys are kept volatile (not persisted to disk) where possible. |
| Host verification | For production, set ssh-known-hosts and strict-host-key-checking: 'yes' to reduce MITM risk. |
| Best practices | Follow least-privilege and key rotation practices. |
- Auto cleanup:
examples/basic-usage.yml,examples/custom-ssh-host-port.yml,examples/url-mappings-auto-cleanup.yml - Manual cleanup:
examples/manual-cleanup-basic.yml,examples/manual-cleanup-custom.yml,examples/url-mappings-manual-cleanup.yml - Generic agent / multiple keys:
examples/generic-ssh-agent.yml,examples/multiple-keys.yml
- If submodules fail to clone, confirm
https-hostmatches the submodule HTTPS URLs. - If host verification fails, populate
ssh-known-hostsusingssh-keyscanand store it in secrets. - If your runner lacks Node.js 20 and you rely on auto-cleanup, use the manual-cleanup flow.
To reduce MITM risk, populate ssh-known-hosts with the exact output of ssh-keyscan for your git host and store it as a secret (e.g., SSH_KNOWN_HOSTS). Example commands:
# Get known_hosts entries for github.com (use your actual host instead of github.com)
ssh-keyscan github.com > known_hosts
# Inspect and copy the file (the output below is a shortened sample; use the full output)
cat known_hosts
# Sample output (truncated):
# github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA7... (truncated)
# github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM... (truncated)
# Set as a repository secret using GitHub CLI (replace owner/repo or run inside the repo)
gh secret set SSH_KNOWN_HOSTS --body "$(cat known_hosts)"
# Alternatively, paste the contents of known_hosts into the repository/organization secretAlso add your private key as SSH_PRIVATE_KEY (single key or multiple keys separated by newlines). For example, to set with gh:
gh secret set SSH_PRIVATE_KEY --body "$(cat ~/.ssh/id_rsa)"Note: Always generate and use the current known_hosts output from your environment; do not rely on pasted example keys in production.
Provide one mapping per line. Each mapping rewrites HTTPS fetch URLs used by Git to SSH equivalents. Replace hosts and user names with your environment's values.
Example 1 — GitHub-like HTTPS submodules rewritten to a self-hosted SSH gateway:
url-mappings: |
https://git.example.com/=ssh://git@nas.example.com:2222/Example 2 — GitLab project host rewritten to same-host SSH (default port 22):
url-mappings: |
https://gitlab.example.com/=ssh://git@gitlab.example.com:22/Example 3 — Multiple mappings (each on its own line):
url-mappings: |
https://git.example.com/=ssh://git@nas.example.com:2222/
https://gitlab.example.com/=ssh://git@gitlab.example.com:22/MIT