283 lines
8.1 KiB
Markdown
283 lines
8.1 KiB
Markdown
# Setup SSH Client
|
|
|
|
A composite GitHub Action that configures SSH client for running remote commands securely.
|
|
|
|
## Features
|
|
|
|
- 🔐 Secure SSH key management
|
|
- 🔧 Automatic SSH configuration
|
|
- 🔑 Known hosts setup with ssh-keyscan fallback
|
|
- ✅ Connection validation
|
|
- 🎯 Simple, reusable configuration
|
|
- 🚀 Custom shell wrapper for seamless remote command execution
|
|
|
|
## Usage
|
|
|
|
### Basic Example with Custom Shell
|
|
|
|
```yaml
|
|
name: Deploy Application
|
|
on: [push]
|
|
|
|
jobs:
|
|
deploy:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Setup SSH
|
|
uses: your-username/setup-ssh-client@v1
|
|
with:
|
|
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
ssh-host: ${{ secrets.SSH_HOST }}
|
|
ssh-user: ${{ secrets.SSH_USER }}
|
|
# Optional: enable debug mode to print the temp script and its
|
|
# contents on the runner for troubleshooting "no output" cases.
|
|
debug-shell-wrapper: 'true'
|
|
|
|
- name: Run remote commands with custom shell
|
|
shell: ssh-remote {0}
|
|
run: |
|
|
cd /var/www
|
|
git pull origin main
|
|
npm install
|
|
systemctl restart myapp
|
|
|
|
- name: Or use direct SSH commands
|
|
run: |
|
|
ssh github-action-host "uptime"
|
|
ssh github-action-host "df -h"
|
|
```
|
|
|
|
### Basic Example (Direct SSH)
|
|
|
|
```yaml
|
|
name: Deploy Application
|
|
on: [push]
|
|
|
|
jobs:
|
|
deploy:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Setup SSH
|
|
uses: your-username/setup-ssh-client@v1
|
|
with:
|
|
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
ssh-host: ${{ secrets.SSH_HOST }}
|
|
ssh-user: ${{ secrets.SSH_USER }}
|
|
|
|
- name: Run remote commands
|
|
run: |
|
|
ssh github-action-host "cd /var/www && git pull"
|
|
ssh github-action-host "systemctl restart myapp"
|
|
```
|
|
|
|
### Advanced Example
|
|
|
|
```yaml
|
|
- name: Setup SSH with custom configuration
|
|
uses: your-username/setup-ssh-client@v1
|
|
with:
|
|
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
ssh-host: example.com
|
|
ssh-user: deploy
|
|
ssh-port: '2222'
|
|
strict-host-key-checking: 'yes'
|
|
ssh-known-hosts: |
|
|
example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC...
|
|
|
|
- name: Deploy application
|
|
run: |
|
|
ssh github-action-host "cd /var/www/app && ./deploy.sh"
|
|
```
|
|
|
|
### Using Different Remote Shells
|
|
|
|
```yaml
|
|
- name: Setup SSH with zsh
|
|
uses: your-username/setup-ssh-client@v1
|
|
with:
|
|
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
ssh-host: example.com
|
|
ssh-user: deploy
|
|
remote-shell: 'zsh'
|
|
|
|
- name: Run commands in remote zsh
|
|
shell: ssh-remote
|
|
run: |
|
|
source ~/.zshrc
|
|
echo "Running in zsh: $SHELL"
|
|
```
|
|
|
|
### Disable Shell Wrapper (SSH Direct Only)
|
|
|
|
```yaml
|
|
- name: Setup SSH without shell wrapper
|
|
uses: your-username/setup-ssh-client@v1
|
|
with:
|
|
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
ssh-host: example.com
|
|
ssh-user: deploy
|
|
use-shell-wrapper: 'false'
|
|
|
|
- name: Use direct SSH commands
|
|
run: |
|
|
ssh github-action-host "command1"
|
|
ssh github-action-host "command2"
|
|
```
|
|
|
|
## Inputs
|
|
|
|
| Input | Description | Required | Default |
|
|
|-------|-------------|----------|---------|
|
|
| `ssh-private-key` | SSH private key for authentication | Yes | - |
|
|
| `ssh-host` | SSH host to connect to | Yes | - |
|
|
| `ssh-user` | SSH username | Yes | - |
|
|
| `ssh-port` | SSH port | No | `22` |
|
|
| `ssh-known-hosts` | Known hosts content (uses ssh-keyscan if not provided) | No | `''` |
|
|
| `strict-host-key-checking` | Enable strict host key checking (`yes`/`no`/`accept-new`) | No | `accept-new` |
|
|
| `use-shell-wrapper` | Create shell wrapper for remote execution (enables `shell: ssh-remote {0}`) | No | `true` |
|
|
| `remote-shell` | Shell to use on remote server (`bash`, `sh`, `zsh`, etc.) | No | `bash` |
|
|
| `debug-shell-wrapper` | Print script path and contents before executing remote commands (for debugging only) | No | `false` |
|
|
|
|
## Outputs
|
|
|
|
| Output | Description |
|
|
|--------|-------------|
|
|
| `ssh-config-path` | Path to the SSH config file |
|
|
| `ssh-key-path` | Path to the SSH private key |
|
|
| `shell-wrapper-path` | Path to the SSH remote shell wrapper script |
|
|
|
|
## Remote Command Execution
|
|
|
|
This action provides two ways to execute commands remotely:
|
|
|
|
### 1. Custom Shell Wrapper (Recommended)
|
|
|
|
Use `shell: ssh-remote {0}` in any step to execute the entire script on the remote server:
|
|
|
|
```yaml
|
|
- name: Deploy application
|
|
shell: ssh-remote {0}
|
|
run: |
|
|
cd /var/www/myapp
|
|
git pull origin main
|
|
npm install --production
|
|
pm2 restart myapp
|
|
```
|
|
|
|
**Benefits:**
|
|
- Natural multi-line script syntax
|
|
shell: ssh-remote {0}
|
|
- Works like a local shell
|
|
- No need to wrap commands in SSH
|
|
|
|
⚠️ Note: Enabling `debug-shell-wrapper: 'true'` will print the contents of the temporary script and the exit code to the job logs. This can help diagnose "no output" runs, but may leak secrets or other sensitive data — only enable on trusted runs.
|
|
|
|
### 2. SSH Host Alias (Direct)
|
|
|
|
|
|
```yaml
|
|
- name: Run single commands
|
|
run: |
|
|
ssh github-action-host "uptime"
|
|
ssh github-action-host "df -h"
|
|
```
|
|
|
|
This eliminates the need to specify the host, user, port, and key path in every SSH command.
|
|
|
|
|
|
### Generating SSH Keys
|
|
|
|
```bash
|
|
# Generate a dedicated SSH key pair for GitHub Actions
|
|
ssh-keygen -t ed25519 -C "github-actions" -f github_actions_key
|
|
|
|
shell: ssh-remote {0}
|
|
```
|
|
|
|
### Setting up Secrets
|
|
|
|
1. Copy your private key content:
|
|
```bash
|
|
cat github_actions_key
|
|
```
|
|
2. Add it to GitHub Secrets:
|
|
- Go to your repository → Settings → Secrets and variables → Actions
|
|
- Click "New repository secret"
|
|
- Name: `SSH_PRIVATE_KEY`
|
|
- Value: Paste the entire private key content
|
|
|
|
3. Add the public key to your server:
|
|
```bash
|
|
cat github_actions_key.pub >> ~/.ssh/authorized_keys
|
|
```
|
|
|
|
### Getting Known Hosts
|
|
|
|
To pre-populate known hosts (recommended for security):
|
|
shell: ssh-remote {0}
|
|
ssh-keyscan -H your-server.com
|
|
```
|
|
|
|
Add the output to a GitHub secret named `SSH_KNOWN_HOSTS`.
|
|
|
|
## Troubleshooting
|
|
|
|
### Debugging no output from custom shell
|
|
|
|
If a step using `shell: ssh-remote {0}` shows no output but reports success:
|
|
|
|
- Enable `debug-shell-wrapper: 'true'` in the `Setup SSH` step. This will tell the wrapper to print the temp script path and the first 200 lines of the script it executes. Example:
|
|
|
|
```yaml
|
|
- name: Setup SSH
|
|
uses: your-username/setup-ssh-client@v1
|
|
with:
|
|
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
ssh-host: ${{ secrets.SSH_HOST }}
|
|
ssh-user: ${{ secrets.SSH_USER }}
|
|
debug-shell-wrapper: 'true'
|
|
```
|
|
|
|
- Re-run the job — the logs from subsequent steps will include the script path and contents; confirm the commands you expect are present.
|
|
- If the script looks correct, try a direct SSH command from a `run:` step to verify remote side:
|
|
|
|
```yaml
|
|
- name: Direct SSH smoke test
|
|
run: ssh github-action-host "echo hello; whoami; pwd"
|
|
```
|
|
|
|
- If the direct test shows output but the wrapper step still shows nothing, examine whether your remote shell sets PATH or environment differently for non-interactive shells — you may need to force a login shell or source shell rc files with `shell: ssh-remote {0}` and `run: | source ~/.bashrc; your commands` or set `remote-shell: 'bash -l'`.
|
|
|
|
Note: Don't forget to disable `debug-shell-wrapper` when done; it prints the script contents into logs which may reveal secrets.
|
|
|
|
### Connection Timeout
|
|
|
|
If the connection test fails with a timeout:
|
|
- Verify the host and port are correct
|
|
- Check firewall rules allow connections from GitHub Actions IPs
|
|
- Ensure SSH service is running on the remote server
|
|
|
|
### Permission Denied
|
|
|
|
If you get "Permission denied":
|
|
- Verify the public key is in `~/.ssh/authorized_keys` on the remote server
|
|
- Check the private key format is correct (should include header/footer)
|
|
- Ensure the SSH user has appropriate permissions
|
|
|
|
### Host Key Verification Failed
|
|
|
|
If you encounter host key verification issues:
|
|
- Set `strict-host-key-checking: 'no'` (not recommended for production)
|
|
- Or provide `ssh-known-hosts` input with the correct host key
|
|
|
|
## License
|
|
|
|
MIT License - see [LICENSE](LICENSE) file for details
|
|
|
|
## Contributing
|
|
|
|
Contributions are welcome! Please feel free to submit a Pull Request.
|