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