A powerful GitHub Action for executing SSH commands and establishing secure port forwarding tunnels with support for jump hosts, dynamic port allocation, and comprehensive authentication methods.
- π Remote Command Execution - Execute commands on remote servers
- π Port Forwarding - Local and remote port forwarding with dynamic allocation
- π¦ Jump Host Support - Multi-hop SSH connections through bastion hosts
- π Multiple Authentication - Private keys, passwords, and SSH agent support
- π Cross-Platform - Works on Linux, macOS, and Windows runners
- π‘οΈ Security First - Known hosts verification and secure key handling
- π Comprehensive Logging - Detailed output for debugging and monitoring
- name: Deploy Application uses: lexbritvin/ssh-action@v1 with: host: your-server.com username: deploy private-key: ${{ secrets.SSH_PRIVATE_KEY }} command: | cd /var/www/app git pull origin main npm install --production pm2 restart app
- name: Forward Database Port uses: lexbritvin/ssh-action@v1 with: host: database-server.com username: dbadmin private-key: ${{ secrets.SSH_PRIVATE_KEY }} local-forwards: "5432:localhost:5432"
- name: Expose Local Service uses: lexbritvin/ssh-action@v1 with: host: tunnel.example.com username: tunnel private-key: ${{ secrets.SSH_PRIVATE_KEY }} remote-forwards: "0:localhost:3000"
name: Database Migration on: [ push ] jobs: migrate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Database Tunnels uses: lexbritvin/ssh-action@v1 with: host: production-bastion.company.com username: devops private-key: ${{ secrets.PRODUCTION_SSH_KEY }} local-forwards: | 5432:postgres-primary.internal:5432, 6379:redis-cluster.internal:6379, 3306:mysql-replica.internal:3306 timeout: 60 keep-alive: 30 - name: Run Database Migration run: | # Now you can connect to localhost:5432, localhost:6379, localhost:3306 npm run migrate:production
- name: Access Internal Server via Bastion uses: lexbritvin/ssh-action@v1 with: host: internal-server.private username: admin private-key: ${{ secrets.SSH_PRIVATE_KEY }} jump-hosts: "bastion1.company.com:22,bastion2.company.com:2222" command: "systemctl status nginx"
name: Production Deployment on: push: branches: [ main ] jobs: deploy: runs-on: ubuntu-latest environment: production steps: - uses: actions/checkout@v4 - name: Deploy to Production uses: lexbritvin/ssh-action@v1 with: host: ${{ secrets.PRODUCTION_HOST }} username: ${{ secrets.PRODUCTION_USER }} private-key: ${{ secrets.PRODUCTION_SSH_KEY }} known-hosts: ${{ secrets.KNOWN_HOSTS }} timeout: 120 command: | set -e echo "π Starting deployment..." # Backup current version sudo cp -r /var/www/app /var/www/app.backup.$(date +%Y%m%d_%H%M%S) # ... echo "β Deployment completed successfully!" post-command: | echo "π§Ή Cleaning up old backups..." find /var/www -name "app.backup.*" -mtime +7 -delete echo "β Cleanup completed"
| Parameter | Description | Required | Default |
|---|---|---|---|
host |
Target SSH host | Yes | - |
port |
SSH port | No | 22 |
username |
SSH username | No | Current user |
| Parameter | Description | Required | Default |
|---|---|---|---|
private-key |
SSH private key content | No | - |
private-key-path |
Path to SSH private key file | No | - |
password |
SSH password (not recommended) | No | - |
known-hosts |
SSH known_hosts content | No | - |
| Parameter | Description | Required | Default |
|---|---|---|---|
local-forwards |
Local port forwards (-L) |
No | - |
remote-forwards |
Remote port forwards (-R) |
No | - |
| Parameter | Description | Required | Default |
|---|---|---|---|
jump-hosts |
Comma-separated jump hosts | No | - |
extra-flags |
Additional SSH flags | No | - |
command |
Command to execute | No | - |
post-command |
Cleanup command | No | - |
timeout |
Connection timeout (seconds) | No | 30 |
keep-alive |
Keep-alive interval (seconds) | No | 60 |
dry-run |
Show command without executing | No | false |
| Output | Description |
|---|---|
pid |
Process ID of the SSH tunnel |
allocated-host |
Public host for remote forward (dynamic allocation) |
allocated-port |
Allocated port for remote forward (dynamic allocation) |
Forward local ports to remote destinations:
local-forwards: "8080:web-server:80" # localhost:8080 β web-server:80 local-forwards: "127.0.0.1:8080:database:5432" # 127.0.0.1:8080 β database:5432 local-forwards: "3000:localhost:3000,8080:nginx:80" # Multiple forwards
Forward remote ports to local destinations:
remote-forwards: "8080:localhost:3000" # remote:8080 β localhost:3000 remote-forwards: "0:localhost:3000" # Dynamic port allocation remote-forwards: "0.0.0.0:9000:localhost:9000" # Bind to all interfaces
# Store your private key in GitHub Secrets - name: Secure SSH Connection uses: lexbritvin/ssh-action@v1 with: host: secure-server.com username: deploy private-key: ${{ secrets.SSH_PRIVATE_KEY }} known-hosts: ${{ secrets.KNOWN_HOSTS }}
Enable detailed logging and disable hosts checking for troubleshooting:
- name: Debug SSH Connection uses: lexbritvin/ssh-action@v1 with: host: problematic-server.com username: debug-user private-key: ${{ secrets.SSH_PRIVATE_KEY }} # Do not check a server key and use verbose SSH output extra-flags: "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -vvv" dry-run: true # Test command generation
- π SSH Port Forwarding Guide
- π SSH Key Management
- π‘οΈ GitHub Secrets Management
- π SSH Jump Host Configuration
This project is licensed under the MIT License - see the LICENSE file for details.
β Star this repo if you find it useful!
Made with β€οΈ for the GitHub Actions community