setup-ssh-client/README.md

8.1 KiB

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

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)

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

- 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

- 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)

- 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:

Use shell: ssh-remote {0} in any step to execute the entire script on the remote server:

    - 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)

- 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

# 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:

    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:

    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:
- 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 file for details

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.