2019-05-14 22:32:43 +00:00
# 🚀 SSH for GitHub Actions
2019-05-14 16:31:53 +00:00
2025-03-08 07:50:53 +00:00
English | [繁體中文 ](./README.zh-tw.md ) | [简体中文 ](./README.zh-cn.md )
2023-03-03 02:45:48 +00:00
2025-03-08 07:42:40 +00:00
A [GitHub Action ](https://github.com/features/actions ) for executing remote SSH commands.
2019-05-14 22:32:43 +00:00
2019-09-29 03:09:26 +00:00

2024-11-17 07:51:42 +00:00
[](https://github.com/appleboy/ssh-action/actions/workflows/main.yml)
2020-05-08 11:05:47 +00:00
2025-03-08 07:42:40 +00:00
This project is built with [Golang ](https://go.dev ) and [drone-ssh ](https://github.com/appleboy/drone-ssh ). 🚀
2023-04-04 09:56:49 +00:00
2021-04-03 21:59:44 +00:00
## Input variables
2025-03-08 07:42:40 +00:00
Refer to [action.yml ](./action.yml ) for more detailed information.
2021-04-03 21:59:44 +00:00
2024-03-16 08:04:59 +00:00
| Input Parameter | Description | Default Value |
2024-10-06 13:19:31 +00:00
| ------------------------- | ---------------------------------------------------------------------------------------- | ------------- |
2024-03-16 08:04:59 +00:00
| host | SSH host address | |
| port | SSH port number | 22 |
| passphrase | SSH key passphrase | |
| username | SSH username | |
| password | SSH password | |
| protocol | SSH protocol version (tcp, tcp4, tcp6) | tcp |
2025-03-07 00:10:39 +00:00
| sync | Enable synchronous execution if multiple hosts are specified | false |
2024-03-16 08:04:59 +00:00
| use_insecure_cipher | Include more ciphers with use_insecure_cipher | false |
2025-03-07 00:10:39 +00:00
| cipher | Allowed cipher algorithms. If unspecified, sensible defaults are used | |
2024-03-16 08:04:59 +00:00
| timeout | Timeout duration for SSH to host | 30s |
| command_timeout | Timeout duration for SSH command | 10m |
| key | Content of SSH private key. e.g., raw content of ~/.ssh/id_rsa | |
| key_path | Path of SSH private key | |
| fingerprint | SHA256 fingerprint of the host public key | |
| proxy_host | SSH proxy host | |
| proxy_port | SSH proxy port | 22 |
2024-10-28 14:37:55 +00:00
| proxy_protocol | SSH proxy protocol version (tcp, tcp4, tcp6) | tcp |
2024-03-16 08:04:59 +00:00
| proxy_username | SSH proxy username | |
| proxy_password | SSH proxy password | |
| proxy_passphrase | SSH proxy key passphrase | |
| proxy_timeout | Timeout for SSH to proxy host | 30s |
| proxy_key | Content of SSH proxy private key | |
| proxy_key_path | Path of SSH proxy private key | |
| proxy_fingerprint | SHA256 fingerprint of the proxy host public key | |
| proxy_cipher | Allowed cipher algorithms for the proxy | |
| proxy_use_insecure_cipher | Include more ciphers with use_insecure_cipher for the proxy | false |
| script | Execute commands | |
2025-01-03 02:17:01 +00:00
| script_path | Execute commands from a file | |
2025-03-07 00:10:39 +00:00
| envs | Pass environment variables to the shell script | |
2024-03-16 08:04:59 +00:00
| envs_format | Flexible configuration of environment value transfer | |
| debug | Enable debug mode | false |
2024-11-17 07:53:54 +00:00
| allenvs | Pass the environment variables with prefix value of `GITHUB_` and `INPUT_` to the script | false |
2024-03-16 08:04:59 +00:00
| request_pty | Request a pseudo-terminal from the server | false |
2021-04-03 21:59:44 +00:00
2025-02-24 12:31:51 +00:00
**Note:** Users can add `set -e` in their shell script to achieve similar functionality to the removed `script_stop` option.
2019-05-14 22:32:43 +00:00
## Usage
2024-11-17 07:53:54 +00:00
Executing remote SSH commands.
2019-05-14 22:32:43 +00:00
2019-09-29 03:09:26 +00:00
```yaml
2019-09-29 06:16:47 +00:00
name: remote ssh command
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
2024-12-01 02:10:43 +00:00
- name: executing remote ssh commands using password
2025-03-09 14:08:40 +00:00
uses: appleboy/ssh-action@v1.2.2
2024-12-01 02:10:43 +00:00
with:
host: ${{ secrets.HOST }}
username: linuxserver.io
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: whoami
2019-05-14 22:32:43 +00:00
```
2019-09-29 03:09:26 +00:00
output:
```sh
======CMD======
whoami
======END======
2024-11-17 07:05:56 +00:00
linuxserver.io
2024-10-13 01:00:02 +00:00
===============================================
✅ Successfully executed commands to all hosts.
===============================================
2019-05-14 22:32:43 +00:00
```
2021-04-03 21:59:44 +00:00
### Setting up a SSH Key
2019-05-14 22:32:43 +00:00
2025-03-08 07:42:40 +00:00
Follow the steps below to create and use SSH Keys.
It is best practice to create SSH Keys on your local machine, not on a remote machine.
Log in with the username specified in GitHub Secrets and generate an RSA Key-Pair:
2019-05-14 22:47:53 +00:00
2023-03-03 02:26:23 +00:00
### Generate rsa key
2019-05-14 22:47:53 +00:00
2021-04-03 21:59:44 +00:00
```bash
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
```
2019-12-07 12:05:02 +00:00
2023-03-03 02:26:23 +00:00
### Generate ed25519 key
2020-08-12 17:06:27 +00:00
2021-04-03 21:59:44 +00:00
```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
```
2020-08-12 17:06:27 +00:00
2025-03-08 07:42:40 +00:00
Add the newly generated key to the Authorized keys. Read more about authorized keys [here ](https://www.ssh.com/ssh/authorized_keys/ ).
2020-08-12 17:06:27 +00:00
2023-03-03 02:26:23 +00:00
### Add rsa key into Authorized keys
2021-04-03 21:59:44 +00:00
2020-08-12 17:06:27 +00:00
```bash
2020-10-30 23:03:29 +00:00
cat .ssh/id_rsa.pub | ssh b@B 'cat >> .ssh/authorized_keys'
2020-08-12 17:06:27 +00:00
```
2023-03-03 02:26:23 +00:00
### Add ed25519 key into Authorized keys
2021-04-03 21:59:44 +00:00
```bash
cat .ssh/id_ed25519.pub | ssh b@B 'cat >> .ssh/authorized_keys'
```
2025-03-08 07:42:40 +00:00
Copy the Private Key content and paste it into GitHub Secrets.
2020-08-12 17:06:27 +00:00
2023-03-03 02:26:23 +00:00
### Copy rsa Private key
2021-04-03 21:59:44 +00:00
2025-03-08 07:42:40 +00:00
Before copying the private key, install the `clip` command as shown below:
2024-07-14 08:59:55 +00:00
```bash
# Ubuntu
sudo apt-get install xclip
```
2025-03-08 07:42:40 +00:00
Copy the private key:
2024-07-14 08:59:55 +00:00
2020-08-12 17:06:27 +00:00
```bash
2024-07-14 08:59:55 +00:00
# macOS
pbcopy < ~/.ssh/id_rsa
# Ubuntu
xclip < ~/.ssh/id_rsa
2020-08-12 17:06:27 +00:00
```
2025-03-08 07:55:34 +00:00
Starting from and including the comment section `-----BEGIN OPENSSH PRIVATE KEY-----` and ending at and including the comment section `-----END OPENSSH PRIVATE KEY-----` , copy the private key and paste it into GitHub Secrets.
2023-03-03 02:26:23 +00:00
### Copy ed25519 Private key
2021-04-03 21:59:44 +00:00
```bash
2024-07-14 08:59:55 +00:00
# macOS
pbcopy < ~/.ssh/id_ed25519
# Ubuntu
xclip < ~/.ssh/id_ed25519
2021-04-03 21:59:44 +00:00
```
2025-03-08 07:42:40 +00:00
See detailed information about [SSH login without a password ](http://www.linuxproblem.org/art_9.html ).
2021-05-09 13:11:10 +00:00
2025-03-08 07:42:40 +00:00
**Note**: Depending on your version of SSH, you might also need to make the following changes:
2021-05-09 13:11:10 +00:00
2024-12-01 02:10:43 +00:00
- Put the public key in `.ssh/authorized_keys2`
- Change the permissions of `.ssh` to 700
- Change the permissions of `.ssh/authorized_keys2` to 640
2020-08-12 17:06:27 +00:00
2022-07-29 12:58:30 +00:00
### If you are using OpenSSH
2022-07-29 13:24:53 +00:00
2022-07-29 12:58:30 +00:00
If you are currently using OpenSSH and are getting the following error:
```bash
ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey]
```
2025-03-08 07:42:40 +00:00
Ensure that your chosen key algorithm is supported. On Ubuntu 20.04 or later, you must explicitly allow the use of the ssh-rsa algorithm. Add the following line to your OpenSSH daemon file (either `/etc/ssh/sshd_config` or a drop-in file under `/etc/ssh/sshd_config.d/` ):
2022-07-29 12:58:30 +00:00
2022-07-29 13:24:53 +00:00
```bash
2022-07-29 12:58:30 +00:00
CASignatureAlgorithms +ssh-rsa
```
2025-03-08 07:42:40 +00:00
Alternatively, `ed25519` keys are accepted by default in OpenSSH. You can use this instead of rsa if needed:
2022-07-29 13:24:53 +00:00
2022-07-29 12:58:30 +00:00
```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
```
2019-09-29 03:09:26 +00:00
### Example
2019-05-14 22:47:53 +00:00
2021-04-03 21:59:44 +00:00
#### Executing remote ssh commands using password
2019-09-29 03:09:26 +00:00
```yaml
- name: executing remote ssh commands using password
2025-03-09 14:08:40 +00:00
uses: appleboy/ssh-action@v1.2.2
2019-09-29 03:09:26 +00:00
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
2019-09-29 03:45:42 +00:00
script: whoami
2019-05-14 22:47:53 +00:00
```
2021-04-03 21:59:44 +00:00
#### Using private key
2019-05-14 22:47:53 +00:00
2019-09-29 03:09:26 +00:00
```yaml
- name: executing remote ssh commands using ssh key
2025-03-09 14:08:40 +00:00
uses: appleboy/ssh-action@v1.2.2
2019-09-29 03:09:26 +00:00
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
script: whoami
2019-05-14 22:47:53 +00:00
```
2021-04-03 21:59:44 +00:00
#### Multiple Commands
2019-05-14 22:52:59 +00:00
2019-09-29 03:09:26 +00:00
```yaml
- name: multiple command
2025-03-09 14:08:40 +00:00
uses: appleboy/ssh-action@v1.2.2
2019-09-29 03:09:26 +00:00
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
script: |
whoami
ls -al
2019-05-14 22:52:59 +00:00
```
2019-09-29 03:09:26 +00:00

2019-05-14 22:59:18 +00:00
2024-11-17 06:57:46 +00:00
#### Commands from a file
```yaml
- name: file commands
2025-03-09 14:08:40 +00:00
uses: appleboy/ssh-action@v1.2.2
2024-11-17 06:57:46 +00:00
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
2024-12-01 02:10:43 +00:00
script_path: scripts/script.sh
2024-11-17 06:57:46 +00:00
```
2021-04-03 21:59:44 +00:00
#### Multiple Hosts
2019-05-14 23:13:49 +00:00
2019-09-29 03:09:26 +00:00
```diff
2019-11-20 15:49:31 +00:00
- name: multiple host
2025-03-09 14:08:40 +00:00
uses: appleboy/ssh-action@v1.2.2
2019-11-20 15:49:31 +00:00
with:
- host: "foo.com"
+ host: "foo.com,bar.com"
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
script: |
whoami
ls -al
2019-05-14 23:13:49 +00:00
```
2019-09-29 03:23:40 +00:00
2024-06-06 00:30:06 +00:00
The default value of `port` is `22` .
2021-04-03 21:59:44 +00:00
#### Multiple hosts with different port
2020-11-17 02:51:06 +00:00
```diff
- name: multiple host
2025-03-09 14:08:40 +00:00
uses: appleboy/ssh-action@v1.2.2
2020-11-17 02:51:06 +00:00
with:
- host: "foo.com"
+ host: "foo.com:1234,bar.com:5678"
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
script: |
whoami
ls -al
```
2021-04-03 21:59:44 +00:00
#### Synchronous execution on multiple hosts
2020-01-30 14:40:33 +00:00
```diff
- name: multiple host
2025-03-09 14:08:40 +00:00
uses: appleboy/ssh-action@v1.2.2
2020-01-30 14:40:33 +00:00
with:
host: "foo.com,bar.com"
+ sync: true
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
script: |
whoami
ls -al
```
2021-04-03 21:59:44 +00:00
#### Pass environment variable to shell script
2019-09-29 03:23:40 +00:00
```diff
2019-11-20 15:49:31 +00:00
- name: pass environment
2025-03-09 14:08:40 +00:00
uses: appleboy/ssh-action@v1.2.2
2019-11-20 15:49:31 +00:00
+ env:
+ FOO: "BAR"
2019-12-07 23:16:30 +00:00
+ BAR: "FOO"
2020-02-09 03:37:56 +00:00
+ SHA: ${{ github.sha }}
2019-11-20 15:49:31 +00:00
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
2022-07-29 13:19:55 +00:00
+ envs: FOO,BAR,SHA
2019-11-20 15:49:31 +00:00
script: |
echo "I am $FOO"
echo "I am $BAR"
2020-02-09 03:37:56 +00:00
echo "sha: $SHA"
2019-09-29 03:23:40 +00:00
```
2019-09-29 04:02:21 +00:00
2020-07-08 08:46:24 +00:00
_Inside `env` object, you need to pass every environment variable as a string, passing `Integer` data type or any other may output unexpected results._
2021-04-03 21:59:44 +00:00
#### How to connect remote server using `ProxyCommand`?
2019-12-07 12:05:02 +00:00
```bash
+--------+ +----------+ +-----------+
| Laptop | < -- > | Jumphost | < -- > | FooServer |
+--------+ +----------+ +-----------+
```
in your `~/.ssh/config` , you will see the following.
```bash
Host Jumphost
HostName Jumphost
User ubuntu
Port 22
IdentityFile ~/.ssh/keys/jump_host.pem
Host FooServer
HostName FooServer
User ubuntu
Port 22
ProxyCommand ssh -q -W %h:%p Jumphost
```
2021-04-03 21:59:44 +00:00
#### How to convert to YAML format of GitHubActions
2019-12-07 12:05:02 +00:00
```diff
- name: ssh proxy command
2025-03-09 14:08:40 +00:00
uses: appleboy/ssh-action@v1.2.2
2019-12-07 12:05:02 +00:00
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
+ proxy_host: ${{ secrets.PROXY_HOST }}
+ proxy_username: ${{ secrets.PROXY_USERNAME }}
+ proxy_key: ${{ secrets.PROXY_KEY }}
+ proxy_port: ${{ secrets.PROXY_PORT }}
script: |
mkdir abc/def
ls -al
```
2019-12-30 11:54:28 +00:00
2021-04-03 21:59:44 +00:00
#### Protecting a Private Key
The purpose of the passphrase is usually to encrypt the private key.
2022-07-29 13:24:53 +00:00
This makes the key file by itself useless to an attacker.
2021-04-03 21:59:44 +00:00
It is not uncommon for files to leak from backups or decommissioned hardware, and hackers commonly exfiltrate files from compromised systems.
2019-12-30 11:54:28 +00:00
```diff
- name: ssh key passphrase
2025-03-09 14:08:40 +00:00
uses: appleboy/ssh-action@v1.2.2
2019-12-30 11:54:28 +00:00
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
+ passphrase: ${{ secrets.PASSPHRASE }}
script: |
whoami
ls -al
```
2021-04-03 21:59:44 +00:00
2021-05-15 15:05:07 +00:00
#### Using host fingerprint verification
Setting up SSH host fingerprint verification can help to prevent Person-in-the-Middle attacks. Before setting this up, run the command below to get your SSH host fingerprint. Remember to replace `ed25519` with your appropriate key type (`rsa`, `dsa` , etc.) that your server is using and `example.com` with your host.
In modern OpenSSH releases, the _default_ key types to be fetched are `rsa` (since version 5.1), `ecdsa` (since version 6.0), and `ed25519` (since version 6.7).
2022-02-06 07:15:00 +00:00
```sh
2021-05-15 15:05:07 +00:00
ssh example.com ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub | cut -d ' ' -f2
```
Now you can adjust you config:
```diff
- name: ssh key passphrase
2025-03-09 14:08:40 +00:00
uses: appleboy/ssh-action@v1.2.2
2021-05-15 15:05:07 +00:00
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
+ fingerprint: ${{ secrets.FINGERPRINT }}
script: |
whoami
ls -al
```
2024-05-04 11:52:33 +00:00
## Q&A
### Command not found (npm or other command)
See the [issue comment ](https://github.com/appleboy/ssh-action/issues/31#issuecomment-1006565847 ) about interactive vs non interactive shell. Thanks @kocyigityunus for the solution.
2025-03-08 07:42:40 +00:00
If you are running a command in a non-interactive shell, like ssh-action, on many Linux distros,
2024-05-04 11:52:33 +00:00
`/etc/bash.bashrc` file has a specific command that returns only, so some of the files didn't run and some specific commands doesn't add to path,
```sh
# /etc/bash.bashrc
# System-wide .bashrc file for interactive bash(1) shells.
# To enable the settings / commands in this file for login shells as well,
# this file has to be sourced in /etc/profile.
# If not running interactively, don't do anything
[ -z "$PS1" ] & & return`
```
2025-03-08 07:42:40 +00:00
comment out the line that returns early, and everything should work fine. Alternatively, you can use the real paths of the commands you want to use.
2024-05-04 11:52:33 +00:00
2021-04-03 21:59:44 +00:00
## Contributing
2022-02-06 07:15:00 +00:00
2021-04-03 21:59:44 +00:00
We would love for you to contribute to `appleboy/ssh-action` , pull requests are welcome!
## License
2022-02-06 07:15:00 +00:00
2021-04-03 21:59:44 +00:00
The scripts and documentation in this project are released under the [MIT License ](LICENSE )