Compare commits

..

No commits in common. "master" and "v1.2.0" have entirely different histories.

9 changed files with 678 additions and 1077 deletions

View File

@ -1,9 +1,10 @@
---
name: Bug report
about: Create a report to help us improve
title: ""
title: ''
labels: bug
assignees: appleboy
---
## Describe the bug
@ -18,18 +19,19 @@ Please post your Yaml configuration file along with the output results.
name: remote ssh command
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@v1.2.2
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: whoami
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: whoami
```
## Related environment

View File

@ -51,21 +51,6 @@ jobs:
port: 2222
script_path: testdata/test.sh
# https://github.com/appleboy/ssh-action/issues/377
- name: multiple commands
uses: ./
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
password: password
port: 2222
script: |
mkdir -p /tmp/test
echo "hello world" > /tmp/test/hello.txt
cd /tmp/test
ls -al
cat /tmp/test/hello.txt
check-ssh-key:
runs-on: ubuntu-latest
steps:
@ -138,6 +123,22 @@ jobs:
port: 2222
script: whoami
- name: stop script if command error
uses: ./
continue-on-error: true
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
password: password
key: password
port: 2222
script_stop: true
sync: true
debug: true
script: |
mkdir abc/def
ls -al
support-key-passphrase:
runs-on: ubuntu-latest
steps:
@ -214,6 +215,7 @@ jobs:
key: ${{ env.PRIVATE_KEY }}
port: 2222
passphrase: 1234
script_stop: true
script: |
ls \
-lah
@ -290,6 +292,7 @@ jobs:
username: linuxserver.io
key: ${{ env.PRIVATE_KEY }}
passphrase: 1234
script_stop: true
script: |
whoami
@ -485,6 +488,7 @@ jobs:
username: linuxserver.io
key: ${{ env.PRIVATE_KEY }}
port: 2222
script_stop: true
request_pty: true
command_timeout: 30s
script: |
@ -519,221 +523,3 @@ jobs:
command_timeout: 30s
script: |
whoami
testing07:
name: some special character
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
- name: Set Environment Variables
run: |
PASS='3HUS$?8kLu)}'
printf "PASS=${PASS}" >> $GITHUB_ENV
- name: create new ssh server
run: |
docker run -d \
--name=openssh-server \
--hostname=openssh-server \
-p 2222:2222 \
-e SUDO_ACCESS=false \
-e PASSWORD_ACCESS=true \
-e USER_PASSWORD='${{ env.PASS }}' \
-e USER_NAME=linuxserver.io \
--restart unless-stopped \
lscr.io/linuxserver/openssh-server:latest
docker exec openssh-server sh -c "hostname -i" > ip.txt
echo "REMOTE_HOST<<EOF" >> $GITHUB_ENV
cat ip.txt >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "======= container ip address ========="
cat ip.txt
echo "======================================"
sleep 2
- name: ssh by username and password
uses: ./
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
password: ${{ env.PASS }}
port: 2222
script: |
#!/usr/bin/env bash
set -e
whoami
testing-capturing-output:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: create new ssh server
run: |
docker run -d \
--name=openssh-server \
--hostname=openssh-server \
-p 2222:2222 \
-e SUDO_ACCESS=false \
-e PASSWORD_ACCESS=true \
-e USER_PASSWORD=password \
-e USER_NAME=linuxserver.io \
--restart unless-stopped \
lscr.io/linuxserver/openssh-server:latest
docker exec openssh-server sh -c "hostname -i" > ip.txt
echo "REMOTE_HOST<<EOF" >> $GITHUB_ENV
cat ip.txt >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "======= container ip address ========="
cat ip.txt
echo "======================================"
sleep 2
- id: stdout
name: ssh command with stdout
uses: ./
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
password: password
port: 2222
capture_stdout: true
script: |
#!/usr/bin/env bash
set -e
whoami
- name: check stdout
run: |
echo "stdout: ${{ steps.stdout.outputs.stdout }}"
testing-script-stop:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: create new ssh server
run: |
docker run -d \
--name=openssh-server \
--hostname=openssh-server \
-p 2222:2222 \
-e SUDO_ACCESS=false \
-e PASSWORD_ACCESS=true \
-e USER_PASSWORD=password \
-e USER_NAME=linuxserver.io \
--restart unless-stopped \
lscr.io/linuxserver/openssh-server:latest
docker exec openssh-server sh -c "hostname -i" > ip.txt
echo "REMOTE_HOST<<EOF" >> $GITHUB_ENV
cat ip.txt >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "======= container ip address ========="
cat ip.txt
echo "======================================"
sleep 2
- id: stdout01
name: ssh command with stdout 01
uses: ./
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
password: password
port: 2222
capture_stdout: true
script: |
#!/usr/bin/env bash
set -e
echo "TMP TESTING IF"
if [[ "2" == "1" ]]; then
echo "True"
else
echo "False"
fi
- name: check stdout 01
run: |
echo "stdout: ${{ steps.stdout01.outputs.stdout }}"
if echo "${{ steps.stdout01.outputs.stdout }}" | grep -q "True"; then
echo "Output contains 'True'"
exit 1
fi
if echo "${{ steps.stdout01.outputs.stdout }}" | grep -q "False"; then
echo "Output contains 'False'"
fi
- id: stdout02
name: ssh command with stdout 01
uses: ./
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
password: password
port: 2222
capture_stdout: true
script: |
#!/usr/bin/env bash
set -e
echo "TMP TESTING IF"
if [[ "1" == "1" ]]; then
echo "True"
else
echo "False"
fi
- name: check stdout 02
run: |
echo "stdout: ${{ steps.stdout02.outputs.stdout }}"
if echo "${{ steps.stdout02.outputs.stdout }}" | grep -q "False"; then
echo "Output contains 'False'"
exit 1
fi
if echo "${{ steps.stdout02.outputs.stdout }}" | grep -q "True"; then
echo "Output contains 'True'"
fi
testing-script-error:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: create new ssh server
run: |
docker run -d \
--name=openssh-server \
--hostname=openssh-server \
-p 2222:2222 \
-e SUDO_ACCESS=false \
-e PASSWORD_ACCESS=true \
-e USER_PASSWORD=password \
-e USER_NAME=linuxserver.io \
--restart unless-stopped \
lscr.io/linuxserver/openssh-server:latest
docker exec openssh-server sh -c "hostname -i" > ip.txt
echo "REMOTE_HOST<<EOF" >> $GITHUB_ENV
cat ip.txt >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "======= container ip address ========="
cat ip.txt
echo "======================================"
sleep 2
- name: test script error
uses: ./
continue-on-error: true
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
password: password
port: 2222
capture_stdout: true
script: |
#!/usr/bin/env bash
set -e
ls /nonexistent

View File

@ -31,7 +31,7 @@ jobs:
sleep 2
- name: ssh by username and password
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
@ -42,15 +42,6 @@ jobs:
set -e
whoami
- name: ssh commands from a file
uses: appleboy/ssh-action@v1
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
password: password
port: 2222
script_path: testdata/test.sh
check-ssh-key:
runs-on: ubuntu-latest
steps:
@ -95,7 +86,7 @@ jobs:
sleep 2
- name: ssh by private key
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
@ -104,7 +95,7 @@ jobs:
script: whoami
- name: wrong password but correct key
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
@ -114,7 +105,7 @@ jobs:
script: whoami
- name: correct password but wrong key
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
@ -123,6 +114,22 @@ jobs:
port: 2222
script: whoami
- name: stop script if command error
uses: appleboy/ssh-action@v1.0.3
continue-on-error: true
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
password: password
key: password
port: 2222
script_stop: true
sync: true
debug: true
script: |
mkdir abc/def
ls -al
support-key-passphrase:
runs-on: ubuntu-latest
steps:
@ -167,7 +174,7 @@ jobs:
sleep 2
- name: ssh key passphrase
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
@ -179,7 +186,7 @@ jobs:
ls -al
- name: missing ssh key passphrase
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
continue-on-error: true
with:
host: ${{ env.REMOTE_HOST }}
@ -192,13 +199,14 @@ jobs:
# https://github.com/appleboy/ssh-action/issues/75#issuecomment-668314271
- name: Multiline SSH commands interpreted as single lines
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
key: ${{ env.PRIVATE_KEY }}
port: 2222
passphrase: 1234
script_stop: true
script: |
ls \
-lah
@ -269,12 +277,13 @@ jobs:
# https://github.com/appleboy/ssh-action/issues/85
- name: Deployment to multiple hosts with different ports
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
with:
host: "${{ env.REMOTE_HOST_01 }}:2222,${{ env.REMOTE_HOST_02 }}:2222"
username: linuxserver.io
key: ${{ env.PRIVATE_KEY }}
passphrase: 1234
script_stop: true
script: |
whoami
@ -322,7 +331,7 @@ jobs:
sleep 2
- name: testing id_ed25519 key
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
@ -375,7 +384,7 @@ jobs:
sleep 2
- name: testing id_ed25519 key
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
@ -386,7 +395,7 @@ jobs:
ls -al
- name: pass environment
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
env:
FOO: "BAR"
with:
@ -400,7 +409,7 @@ jobs:
echo "I am $BAR, thanks"
- name: pass multiple environment
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
env:
FOO: "BAR"
BAR: "FOO"
@ -419,7 +428,7 @@ jobs:
echo "port: $PORT"
- name: custom envs format
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
env:
FOO: "BAR"
AAA: "BBB"
@ -437,7 +446,7 @@ jobs:
echo "I am $TEST_AAA, thanks"
- name: pass all ENV variables to script
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
env:
INPUT_FOO: "BAR"
INPUT_AAA: "BBB"
@ -454,12 +463,13 @@ jobs:
echo "$GITHUB_REF"
- name: switch to root user
uses: appleboy/ssh-action@v1
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
key: ${{ env.PRIVATE_KEY }}
port: 2222
script_stop: true
request_pty: true
command_timeout: 30s
script: |

View File

@ -24,5 +24,5 @@ changelog:
order: 4
- title: "Documentation updates"
regexp: ^.*?docs?(\(.+\))??!?:.+$
order: 5
order: 4
- title: Others

406
README.md
View File

@ -1,147 +1,83 @@
# 🚀 SSH for GitHub Actions
English | [繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md)
[繁體中文](./README.zh-tw.md)
[简体中文](./README.zh-cn.md)
## Table of Contents
- [🚀 SSH for GitHub Actions](#-ssh-for-github-actions)
- [Table of Contents](#table-of-contents)
- [📖 Introduction](#-introduction)
- [🧩 Core Concepts \& Input Parameters](#-core-concepts--input-parameters)
- [🔌 Connection Settings](#-connection-settings)
- [🛠️ SSH Command Settings](#-ssh-command-settings)
- [🌐 Proxy Settings](#-proxy-settings)
- [⚡ Quick Start](#-quick-start)
- [🔑 SSH Key Setup \& OpenSSH Compatibility](#-ssh-key-setup--openssh-compatibility)
- [Setting Up SSH Keys](#setting-up-ssh-keys)
- [Generate RSA key](#generate-rsa-key)
- [Generate ED25519 key](#generate-ed25519-key)
- [OpenSSH Compatibility](#openssh-compatibility)
- [🛠️ Usage Scenarios \& Advanced Examples](#-usage-scenarios--advanced-examples)
- [Using password authentication](#using-password-authentication)
- [Using private key authentication](#using-private-key-authentication)
- [Multiple commands](#multiple-commands)
- [Run commands from a file](#run-commands-from-a-file)
- [Multiple hosts](#multiple-hosts)
- [Multiple hosts with different ports](#multiple-hosts-with-different-ports)
- [Synchronous execution on multiple hosts](#synchronous-execution-on-multiple-hosts)
- [Pass environment variables to shell script](#pass-environment-variables-to-shell-script)
- [🌐 Proxy \& Jump Host Usage](#-proxy--jump-host-usage)
- [🛡️ Security Best Practices](#-security-best-practices)
- [Protecting Your Private Key](#protecting-your-private-key)
- [Host Fingerprint Verification](#host-fingerprint-verification)
- [🚨 Error Handling \& Troubleshooting](#-error-handling--troubleshooting)
- [Q\&A](#qa)
- [Command not found (npm or other command)](#command-not-found-npm-or-other-command)
- [🤝 Contributing](#-contributing)
- [📝 License](#-license)
---
## 📖 Introduction
**SSH for GitHub Actions** is a powerful [GitHub Action](https://github.com/features/actions) for executing remote SSH commands easily and securely in your CI/CD workflows.
Built with [Golang](https://go.dev) and [drone-ssh](https://github.com/appleboy/drone-ssh), it supports a wide range of SSH scenarios, including multi-host, proxy, and advanced authentication.
[GitHub Action](https://github.com/features/actions) for executing remote ssh commands.
![ssh workflow](./images/ssh-workflow.png)
[![testing main branch](https://github.com/appleboy/ssh-action/actions/workflows/main.yml/badge.svg)](https://github.com/appleboy/ssh-action/actions/workflows/main.yml)
[![Actions Status](https://github.com/appleboy/ssh-action/workflows/remote%20ssh%20command/badge.svg)](https://github.com/appleboy/ssh-action/actions)
---
**Important**: Only support **Linux** [docker](https://www.docker.com/) container.
## 🧩 Core Concepts & Input Parameters
This thing is built using [Golang](https://go.dev) and [drone-ssh](https://github.com/appleboy/drone-ssh). 🚀
This action provides flexible SSH command execution with a rich set of configuration options.
## Input variables
For full details, see [action.yml](./action.yml).
See [action.yml](./action.yml) for more detailed information.
### 🔌 Connection Settings
| Input Parameter | Description | Default Value |
| ------------------------- | ---------------------------------------------------------------------------------------- | ------------- |
| 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 |
| sync | Enable synchronous execution if multiple hosts | false |
| use_insecure_cipher | Include more ciphers with use_insecure_cipher | false |
| cipher | Allowed cipher algorithms. If unspecified, a sensible default | |
| 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 |
| proxy_protocol | SSH proxy protocol version (tcp, tcp4, tcp6) | tcp |
| 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 | |
| script_file | Execute commands from a file | |
| script_stop | Stop script after first failure | false |
| envs | Pass environment variables to shell script | |
| envs_format | Flexible configuration of environment value transfer | |
| debug | Enable debug mode | false |
| allenvs | pass the environment variables with prefix value of `GITHUB_` and `INPUT_` to the script | false |
| request_pty | Request a pseudo-terminal from the server | false |
These parameters control how the action connects to your remote host.
## Usage
| Parameter | Description | Default |
| ------------------- | ----------------------------------------------------------------- | ------- |
| host | SSH host address | |
| port | SSH port number | 22 |
| username | SSH username | |
| password | SSH password | |
| protocol | SSH protocol version (`tcp`, `tcp4`, `tcp6`) | tcp |
| sync | Run synchronously if multiple hosts are specified | false |
| timeout | Timeout for SSH connection to host | 30s |
| key | Content of SSH private key (e.g., raw content of `~/.ssh/id_rsa`) | |
| key_path | Path to SSH private key | |
| passphrase | Passphrase for the SSH private key | |
| fingerprint | SHA256 fingerprint of the host public key | |
| use_insecure_cipher | Allow additional (less secure) ciphers | false |
| cipher | Allowed cipher algorithms. Uses sensible defaults if unspecified | |
---
### 🛠️ SSH Command Settings
These parameters control the commands executed on the remote host and related behaviors.
| Parameter | Description | Default |
| --------------- | --------------------------------------------------------------------------------- | ------- |
| script | Commands to execute remotely | |
| script_path | Path to a file containing commands to execute | |
| envs | Environment variables to pass to the shell script | |
| envs_format | Flexible configuration for environment variable transfer | |
| allenvs | Pass all environment variables with `GITHUB_` and `INPUT_` prefixes to the script | false |
| command_timeout | Timeout for SSH command execution | 10m |
| debug | Enable debug mode | false |
| request_pty | Request a pseudo-terminal from the server | false |
| curl_insecure | Allow curl to connect to SSL sites without certificates | false |
| version | drone-ssh binary version. If not specified, the latest version will be used. | |
---
### 🌐 Proxy Settings
These parameters control the use of a proxy (jump host) for connecting to your target host.
| Parameter | Description | Default |
| ------------------------- | ----------------------------------------------- | ------- |
| proxy_host | SSH proxy host | |
| proxy_port | SSH proxy port | 22 |
| proxy_username | SSH proxy username | |
| proxy_password | SSH proxy password | |
| proxy_passphrase | SSH proxy key passphrase | |
| proxy_protocol | SSH proxy protocol version | tcp |
| proxy_timeout | Timeout for SSH connection to proxy host | 30s |
| proxy_key | Content of SSH proxy private key | |
| proxy_key_path | Path to 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 | Allow insecure ciphers for the proxy | false |
> **Note:** To mimic the removed `script_stop` option, add `set -e` at the top of your shell script.
---
## ⚡ Quick Start
Run remote SSH commands in your workflow with minimal configuration:
Executing remote ssh commands.
```yaml
name: Remote SSH Command
name: remote ssh command
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Execute remote SSH commands using password
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: linuxserver.io
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: whoami
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: linuxserver.io
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: whoami
```
**Output:**
output:
```sh
======CMD======
@ -153,37 +89,50 @@ linuxserver.io
===============================================
```
---
### Setting up a SSH Key
## 🔑 SSH Key Setup & OpenSSH Compatibility
Make sure to follow the below steps while creating SSH Keys and using them.
The best practice is create the SSH Keys on local machine not remote machine.
Login with username specified in Github Secrets. Generate a RSA Key-Pair:
### Setting Up SSH Keys
It is best practice to create SSH keys on your local machine (not on a remote server). Log in with the username specified in GitHub Secrets and generate a key pair:
#### Generate RSA key
### Generate rsa key
```bash
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
```
#### Generate ED25519 key
### Generate ed25519 key
```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
```
Add the new public key to the authorized keys on your server. [Learn more about authorized keys.](https://www.ssh.com/ssh/authorized_keys/)
Add newly generated key into Authorized keys. Read more about authorized keys [here](https://www.ssh.com/ssh/authorized_keys/).
### Add rsa key into Authorized keys
```bash
# Add RSA key
cat .ssh/id_rsa.pub | ssh user@host 'cat >> .ssh/authorized_keys'
# Add ED25519 key
cat .ssh/id_ed25519.pub | ssh user@host 'cat >> .ssh/authorized_keys'
cat .ssh/id_rsa.pub | ssh b@B 'cat >> .ssh/authorized_keys'
```
Copy the private key content and paste it into GitHub Secrets.
### Add ed25519 key into Authorized keys
```bash
cat .ssh/id_ed25519.pub | ssh b@B 'cat >> .ssh/authorized_keys'
```
Copy Private Key content and paste in Github Secrets.
### Copy rsa Private key
Before copying the private key, install `clip` command as shown below:
```bash
# Ubuntu
sudo apt-get install xclip
```
copy the private key:
```bash
# macOS
@ -192,9 +141,7 @@ pbcopy < ~/.ssh/id_rsa
xclip < ~/.ssh/id_rsa
```
> **Tip:** Copy from `-----BEGIN OPENSSH PRIVATE KEY-----` to `-----END OPENSSH PRIVATE KEY-----` (inclusive).
For ED25519:
### Copy ed25519 Private key
```bash
# macOS
@ -203,45 +150,41 @@ pbcopy < ~/.ssh/id_ed25519
xclip < ~/.ssh/id_ed25519
```
See more: [SSH login without a password](http://www.linuxproblem.org/art_9.html).
See the detail information about [SSH login without password](http://www.linuxproblem.org/art_9.html).
> **Note:** Depending on your SSH version, you may also need to:
>
> - Place the public key in `.ssh/authorized_keys2`
> - Set `.ssh` permissions to 700
> - Set `.ssh/authorized_keys2` permissions to 640
**A note** from one of our readers: Depending on your version of SSH you might also have to do the following changes:
### OpenSSH Compatibility
* Put the public key in `.ssh/authorized_keys2`
* Change the permissions of `.ssh` to 700
* Change the permissions of `.ssh/authorized_keys2` to 640
If you see this error:
### If you are using OpenSSH
If you are currently using OpenSSH and are getting the following error:
```bash
ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey]
```
On Ubuntu 20.04+ you may need to explicitly allow the `ssh-rsa` algorithm. Add this to your OpenSSH daemon config (`/etc/ssh/sshd_config` or a drop-in under `/etc/ssh/sshd_config.d/`):
Make sure that your key algorithm of choice 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 (which is either `/etc/ssh/sshd_config` or a drop-in file under `/etc/ssh/sshd_config.d/`):
```bash
CASignatureAlgorithms +ssh-rsa
```
Alternatively, use ED25519 keys (supported by default):
Alternatively, `ed25519` keys are accepted by default in OpenSSH. You could use this instead of rsa if needed:
```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
```
---
### Example
## 🛠️ Usage Scenarios & Advanced Examples
This section covers common and advanced usage patterns, including multi-host, proxy, and environment variable passing.
### Using password authentication
#### Executing remote ssh commands using password
```yaml
- name: Execute remote SSH commands using password
uses: appleboy/ssh-action@v1
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -250,11 +193,11 @@ This section covers common and advanced usage patterns, including multi-host, pr
script: whoami
```
### Using private key authentication
#### Using private key
```yaml
- name: Execute remote SSH commands using SSH key
uses: appleboy/ssh-action@v1
- name: executing remote ssh commands using ssh key
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -263,11 +206,11 @@ This section covers common and advanced usage patterns, including multi-host, pr
script: whoami
```
### Multiple commands
#### Multiple Commands
```yaml
- name: Multiple commands
uses: appleboy/ssh-action@v1
- name: multiple command
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -280,24 +223,24 @@ This section covers common and advanced usage patterns, including multi-host, pr
![result](./images/output-result.png)
### Run commands from a file
#### Commands from a file
```yaml
- name: File commands
uses: appleboy/ssh-action@v1
- name: file commands
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
script_path: scripts/script.sh
script_path: scripts/script.sh
```
### Multiple hosts
#### Multiple Hosts
```diff
- name: Multiple hosts
uses: appleboy/ssh-action@v1
- name: multiple host
uses: appleboy/ssh-action@v1.2.0
with:
- host: "foo.com"
+ host: "foo.com,bar.com"
@ -309,13 +252,13 @@ This section covers common and advanced usage patterns, including multi-host, pr
ls -al
```
Default `port` is `22`.
The default value of `port` is `22`.
### Multiple hosts with different ports
#### Multiple hosts with different port
```diff
- name: Multiple hosts
uses: appleboy/ssh-action@v1
- name: multiple host
uses: appleboy/ssh-action@v1.2.0
with:
- host: "foo.com"
+ host: "foo.com:1234,bar.com:5678"
@ -326,11 +269,11 @@ Default `port` is `22`.
ls -al
```
### Synchronous execution on multiple hosts
#### Synchronous execution on multiple hosts
```diff
- name: Multiple hosts
uses: appleboy/ssh-action@v1
- name: multiple host
uses: appleboy/ssh-action@v1.2.0
with:
host: "foo.com,bar.com"
+ sync: true
@ -342,11 +285,11 @@ Default `port` is `22`.
ls -al
```
### Pass environment variables to shell script
#### Pass environment variable to shell script
```diff
- name: Pass environment
uses: appleboy/ssh-action@v1
- name: pass environment
uses: appleboy/ssh-action@v1.2.0
+ env:
+ FOO: "BAR"
+ BAR: "FOO"
@ -363,13 +306,40 @@ Default `port` is `22`.
echo "sha: $SHA"
```
> _All environment variables in the `env` object must be strings. Using integers or other types may cause unexpected results._
_Inside `env` object, you need to pass every environment variable as a string, passing `Integer` data type or any other may output unexpected results._
---
#### Stop script after first failure
## 🌐 Proxy & Jump Host Usage
> ex: missing `abc` folder
You can connect to remote hosts via a proxy (jump host) for advanced network topologies.
```diff
- name: stop script if command error
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
+ script_stop: true
script: |
mkdir abc/def
ls -al
```
output:
```sh
======CMD======
mkdir abc/def
ls -al
======END======
2019/11/21 01:16:21 Process exited with status 1
err: mkdir: cannot create directory abc/def: No such file or directory
##[error]Docker run failed with exit code 1
```
#### How to connect remote server using `ProxyCommand`?
```bash
+--------+ +----------+ +-----------+
@ -377,7 +347,7 @@ You can connect to remote hosts via a proxy (jump host) for advanced network top
+--------+ +----------+ +-----------+
```
Example `~/.ssh/config`:
in your `~/.ssh/config`, you will see the following.
```bash
Host Jumphost
@ -393,11 +363,11 @@ Host FooServer
ProxyCommand ssh -q -W %h:%p Jumphost
```
**GitHub Actions YAML:**
#### How to convert to YAML format of GitHubActions
```diff
- name: SSH proxy command
uses: appleboy/ssh-action@v1
- name: ssh proxy command
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -412,17 +382,15 @@ Host FooServer
ls -al
```
---
#### Protecting a Private Key
## 🛡️ Security Best Practices
### Protecting Your Private Key
A passphrase encrypts your private key, making it useless to attackers if leaked. Always store your private key securely.
The purpose of the passphrase is usually to encrypt the private key.
This makes the key file by itself useless to an attacker.
It is not uncommon for files to leak from backups or decommissioned hardware, and hackers commonly exfiltrate files from compromised systems.
```diff
- name: SSH key passphrase
uses: appleboy/ssh-action@v1
- name: ssh key passphrase
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -434,19 +402,21 @@ A passphrase encrypts your private key, making it useless to attackers if leaked
ls -al
```
### Host Fingerprint Verification
#### Using host fingerprint verification
Verifying the SSH host fingerprint helps prevent man-in-the-middle attacks. To get your host's fingerprint (replace `ed25519` with your key type and `example.com` with your host):
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).
```sh
ssh example.com ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub | cut -d ' ' -f2
```
Update your config:
Now you can adjust you config:
```diff
- name: SSH key passphrase
uses: appleboy/ssh-action@v1
- name: ssh key passphrase
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -458,33 +428,33 @@ Update your config:
ls -al
```
---
## Q&A
## 🚨 Error Handling & Troubleshooting
### Command not found (npm or other command)
### Q&A
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.
#### Command not found (npm or other command)
Basically, if you are running a command in a non interactive shell, like ssh-action, on many linux distros,
If you encounter "command not found" errors, see [this issue comment](https://github.com/appleboy/ssh-action/issues/31#issuecomment-1006565847) about interactive vs non-interactive shells.
On many Linux distros, `/etc/bash.bashrc` contains:
`/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
[ -z "$PS1" ] && return`
```
Comment out this line or use absolute paths for your commands.
just comment out the line that returns early and everything should work fine, or you can use the real paths of the commands that you would like to use.
---
## Contributing
## 🤝 Contributing
We would love for you to contribute to `appleboy/ssh-action`, pull requests are welcome!
Contributions are welcome! Please submit a pull request to help improve `appleboy/ssh-action`.
## License
---
## 📝 License
This project is licensed under the [MIT License](LICENSE).
The scripts and documentation in this project are released under the [MIT License](LICENSE)

View File

@ -1,247 +1,164 @@
# 🚀 用于 GitHub Actions 的 SSH
[English](./README.md) | [繁體中文](./README.zh-tw.md) | 简体中文
## 目录
- [🚀 用于 GitHub Actions 的 SSH](#-用于-github-actions-的-ssh)
- [目录](#目录)
- [📖 简介](#-简介)
- [🧩 核心概念与输入参数](#-核心概念与输入参数)
- [🔌 连接设置](#-连接设置)
- [🛠️ 指令设置](#-指令设置)
- [🌐 代理设置](#-代理设置)
- [⚡ 快速开始](#-快速开始)
- [🔑 SSH 密钥配置与 OpenSSH 兼容性](#-ssh-密钥配置与-openssh-兼容性)
- [配置 SSH 密钥](#配置-ssh-密钥)
- [生成 RSA 密钥](#生成-rsa-密钥)
- [生成 ED25519 密钥](#生成-ed25519-密钥)
- [OpenSSH 兼容性](#openssh-兼容性)
- [🛠️ 用法场景与进阶示例](#-用法场景与进阶示例)
- [使用密码认证](#使用密码认证)
- [使用私钥认证](#使用私钥认证)
- [多条命令](#多条命令)
- [从文件执行命令](#从文件执行命令)
- [多主机](#多主机)
- [多主机不同端口](#多主机不同端口)
- [多主机同步执行](#多主机同步执行)
- [传递环境变量到 shell 脚本](#传递环境变量到-shell-脚本)
- [🌐 代理与跳板机用法](#-代理与跳板机用法)
- [🛡️ 安全最佳实践](#-安全最佳实践)
- [保护你的私钥](#保护你的私钥)
- [主机指纹验证](#主机指纹验证)
- [🚨 错误处理与疑难解答](#-错误处理与疑难解答)
- [常见问题](#常见问题)
- [命令未找到npm 或其他命令)](#命令未找到npm-或其他命令)
- [🤝 贡献](#-贡献)
- [📝 许可证](#-许可证)
---
## 📖 简介
**SSH for GitHub Actions** 是一个强大的 [GitHub Action](https://github.com/features/actions),可让你在 CI/CD 工作流中轻松且安全地执行远程 SSH 命令。
本项目基于 [Golang](https://go.dev) 和 [drone-ssh](https://github.com/appleboy/drone-ssh) 构建,支持多主机、代理、高级认证等多种 SSH 场景。
[GitHub Action](https://github.com/features/actions) 用于执行远程 SSH 命令。
![ssh workflow](./images/ssh-workflow.png)
[![testing main branch](https://github.com/appleboy/ssh-action/actions/workflows/main.yml/badge.svg)](https://github.com/appleboy/ssh-action/actions/workflows/main.yml)
[![Actions Status](https://github.com/appleboy/ssh-action/workflows/remote%20ssh%20command/badge.svg)](https://github.com/appleboy/ssh-action/actions)
---
**注意** 只支持在 **Linux** [docker](https://www.docker.com/) 容器上执行。
## 🧩 核心概念与输入参数
## 输入变量
本 Action 提供灵活的 SSH 命令执行能力,并具备丰富的配置选项
更详细的信息,请参考 [action.yml](./action.yml)。
详细参数请参阅 [action.yml](./action.yml)。
* `host` - SSH 主机
* `port` - SSH 连接端口,默认为 `22`
* `username` - SSH 用户名称
* `password` - SSH 密码
* `passphrase` - 通常用于加密私钥的 passphrase
* `sync` - 同步执行多个主机上的命令,默认为 false
* `timeout` - SSH 连接到远程主机的超时时间,默认为 `30s`
* `command_timeout` - SSH 命令超时时间,默认为 10m
* `key` - SSH 私钥的内容,例如 ~/.ssh/id_rsa 的原始内容,请记得包含 BEGIN 和 END 行
* `key_path` - SSH 私钥的路径
* `fingerprint` - 主机公钥的 SHA256 指纹,默认为跳过验证
* `script` - 执行命令
* `script_file` - 執行命令的文件
* `script_stop` - 当出现第一个错误时停止执行命令
* `envs` - 传递环境变量到 shell script
* `debug` - 启用调试模式
* `use_insecure_cipher` - 使用不安全的密码ciphers进行加密详见 [#56](https://github.com/appleboy/ssh-action/issues/56)
* `cipher` - 允许使用的密码ciphers算法。如果未指定则使用适当的算法
### 🔌 连接设置
SSH 代理设置:
这些参数用于控制如何连接到远程主机。
* `proxy_host` - 代理主机
* `proxy_port` - 代理端口,默认为 `22`
* `proxy_username` - 代理用户名
* `proxy_password` - 代理密码
* `proxy_passphrase` - 密码通常用于加密私有密钥
* `proxy_timeout` - SSH 连接至代理主机的超时时间,默认为 `30s`
* `proxy_key` - SSH 代理私有密钥内容
* `proxy_key_path` - SSH 代理私有密钥路径
* `proxy_fingerprint` - 代理主机公钥的 SHA256 指纹,默认为跳过验证
* `proxy_use_insecure_cipher` - 使用不安全的加密方式,详见 [#56](https://github.com/appleboy/ssh-action/issues/56)
* `proxy_cipher` - 允许的加密算法。如果未指定,则使用合理的算法
| 参数 | 描述 | 默认值 |
| ------------------- | --------------------------------------------- | ------ |
| host | SSH 主机地址 | |
| port | SSH 端口号 | 22 |
| username | SSH 用户名 | |
| password | SSH 密码 | |
| protocol | SSH 协议版本(`tcp``tcp4``tcp6` | tcp |
| sync | 指定多个主机时同步执行 | false |
| timeout | SSH 连接主机的超时时间 | 30s |
| key | SSH 私钥内容(如 `~/.ssh/id_rsa` 的原始内容) | |
| key_path | SSH 私钥路径 | |
| passphrase | SSH 私钥密码短语 | |
| fingerprint | 主机公钥的 SHA256 指纹 | |
| use_insecure_cipher | 允许额外(不安全)的加密算法 | false |
| cipher | 允许的加密算法,未指定时使用默认值 | |
## 使用方法
---
### 🛠️ 指令设置
这些参数用于控制在远程主机上执行的命令及相关行为。
| 参数 | 描述 | 默认值 |
| --------------- | ----------------------------------------------------- | ------ |
| script | 远程执行的命令 | |
| script_path | 包含要执行命令的文件路径 | |
| envs | 传递给 shell 脚本的环境变量 | |
| envs_format | 环境变量传递的灵活配置 | |
| allenvs | 传递所有带 `GITHUB_``INPUT_` 前缀的环境变量到脚本 | false |
| command_timeout | SSH 命令执行超时时间 | 10m |
| debug | 启用调试模式 | false |
| request_pty | 向服务器请求伪终端 | false |
| curl_insecure | 允许 curl 连接无证书的 SSL 站点 | false |
| version | drone-ssh 二进制版本,未指定时使用最新版本 | |
---
### 🌐 代理设置
这些参数用于通过代理(跳板机)连接到目标主机。
| 参数 | 描述 | 默认值 |
| ------------------------- | ----------------------------------------- | ------ |
| proxy_host | SSH 代理主机 | |
| proxy_port | SSH 代理端口 | 22 |
| proxy_username | SSH 代理用户名 | |
| proxy_password | SSH 代理密码 | |
| proxy_passphrase | SSH 代理私钥密码短语 | |
| proxy_protocol | SSH 代理协议版本(`tcp``tcp4``tcp6` | tcp |
| proxy_timeout | SSH 连接代理主机的超时时间 | 30s |
| proxy_key | SSH 代理私钥内容 | |
| proxy_key_path | SSH 代理私钥路径 | |
| proxy_fingerprint | 代理主机公钥的 SHA256 指纹 | |
| proxy_cipher | 代理允许的加密算法 | |
| proxy_use_insecure_cipher | 代理允许额外(不安全)的加密算法 | false |
> **注意:** 如需实现已移除的 `script_stop` 功能,请在 shell 脚本顶部添加 `set -e`
---
## ⚡ 快速开始
只需简单配置,即可在工作流中执行远程 SSH 命令:
执行远程 SSH 命令
```yaml
name: Remote SSH Command
name: remote ssh command
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: 执行远程 SSH 命令(密码认证)
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: linuxserver.io
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: whoami
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: whoami
```
**输出:**
画面输出
```sh
======CMD======
whoami
======END======
linuxserver.io
out: ***
===============================================
✅ Successfully executed commands to all hosts.
===============================================
```
---
### 设置 SSH 密钥
## 🔑 SSH 密钥配置与 OpenSSH 兼容性
请在创建 SSH 密钥并使用 SSH 密钥时遵循以下步骤。最佳做法是在本地机器上创建 SSH 密钥而不是远程机器上。请使用 Github Secrets 中指定的用户名登录。生成 RSA 密钥:
### 配置 SSH 密钥
建议在本地机器(而非远程服务器)上创建 SSH 密钥。请使用 GitHub Secrets 中指定的用户名登录并生成密钥对:
#### 生成 RSA 密钥
### 生成 RSA 密钥
```bash
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
ssh-keygen -t rsa -b 4096 -C ”your_email@example.com“
```
#### 生成 ED25519 密钥
### 生成 ed25519 密钥
```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
ssh-keygen -t ed25519 -a 200 -C ”your_email@example.com“
```
将新生成的公钥添加到服务器的 authorized_keys。 [了解更多 authorized_keys](https://www.ssh.com/ssh/authorized_keys/)
将新生成的密钥添加到已授权的密钥中。详细了解已授权的密钥请点[此处](https://www.ssh.com/ssh/authorized_keys/)。
### 将 RSA 密钥添加到已授权密钥中
```bash
# 添加 RSA 公钥
cat .ssh/id_rsa.pub | ssh user@host 'cat >> .ssh/authorized_keys'
# 添加 ED25519 公钥
cat .ssh/id_ed25519.pub | ssh user@host 'cat >> .ssh/authorized_keys'
cat .ssh/id_rsa.pub | ssh b@B cat >> .ssh/authorized_keys
```
复制私钥内容并粘贴到 GitHub Secrets。
### 将 ed25519 密钥添加到已授权密钥中
```bash
# macOS
pbcopy < ~/.ssh/id_rsa
# Ubuntu
xclip < ~/.ssh/id_rsa
cat .ssh/id_ed25519.pub | ssh b@B cat >> .ssh/authorized_keys
```
> **提示:** 复制内容需包含 `-----BEGIN OPENSSH PRIVATE KEY-----``-----END OPENSSH PRIVATE KEY-----`(含)
复制私钥内容,然后将其粘贴到 Github Secrets 中。
ED25519 同理:
### 复制 rsa 私钥内容
```bash
# macOS
pbcopy < ~/.ssh/id_ed25519
# Ubuntu
xclip < ~/.ssh/id_ed25519
clip < ~/.ssh/id_rsa
```
更多信息:[SSH 无密码登录](http://www.linuxproblem.org/art_9.html)。
### 复制 ed25519 私钥内容
> **注意:** 根据 SSH 版本,可能还需:
>
> - 将公钥放入 `.ssh/authorized_keys2`
> - 设置 `.ssh` 权限为 700
> - 设置 `.ssh/authorized_keys2` 权限为 640
```bash
clip < ~/.ssh/id_ed25519
```
### OpenSSH 兼容性
有关无需密码登录 SSH 的详细信息,请[见该网站](http://www.linuxproblem.org/art_9.html)。
如果出现如下错误:
**来自读者的注意事项** 根据您的 SSH 版本,您可能还需要进行以下更改:
* 将公钥放在 `.ssh/authorized_keys2`
* 将 `.ssh` 的权限更改为700
* 将 `.ssh/authorized_keys2` 的权限更改为640
### 如果你使用的是 OpenSSH
如果您正在使用 OpenSSH并出现以下错误
```bash
ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey]
```
在 Ubuntu 20.04+,你可能需要显式允许 `ssh-rsa` 算法。请在 OpenSSH 配置文件(`/etc/ssh/sshd_config``/etc/ssh/sshd_config.d/` 下的 drop-in 文件)中添加:
请确保您所选择的密钥算法得到支持。在 Ubuntu 20.04 或更高版本上,您必须明确允许使用 SSH-RSA 算法。请在 OpenSSH 守护进程文件中添加以下行(它可以是 `/etc/ssh/sshd_config``/etc/ssh/sshd_config.d/` 中的一个附加文件)
```bash
CASignatureAlgorithms +ssh-rsa
```
或者,直接使用默认支持的 ED25519 密钥:
或者,`Ed25519` 密钥在 OpenSSH 中默认被接受。如果需要,您可以使用它来替代 RSA。
```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
ssh-keygen -t ed25519 -a 200 -C ”your_email@example.com“
```
---
### Example
## 🛠️ 用法场景与进阶示例
本节涵盖常见与进阶用法,包括多主机、代理、环境变量传递等。
### 使用密码认证
#### 使用密码执行远程 SSH 命令
```yaml
- name: 执行远程 SSH 命令(密码认证)
uses: appleboy/ssh-action@v1
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -250,11 +167,11 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
script: whoami
```
### 使用私钥认证
#### 使用私钥
```yaml
- name: 执行远程 SSH 命令(密钥认证)
uses: appleboy/ssh-action@v1
- name: executing remote ssh commands using ssh key
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -263,11 +180,11 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
script: whoami
```
### 多条命令
#### 多个命令
```yaml
- name: 多条命令
uses: appleboy/ssh-action@v1
- name: multiple command
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -280,27 +197,14 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
![result](./images/output-result.png)
### 从文件执行命令
```yaml
- name: 文件命令
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
script_path: scripts/script.sh
```
### 多主机
#### 多台主机
```diff
- name: 多主机
uses: appleboy/ssh-action@v1
- name: multiple host
uses: appleboy/ssh-action@v1.2.0
with:
- host: "foo.com"
+ host: "foo.com,bar.com"
- host: ”foo.com“
+ host: ”foo.com,bar.com“
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
@ -309,16 +213,27 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
ls -al
```
默认 `port``22`
#### Commands from a file
### 多主机不同端口
```yaml
- name: file commands
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
script_path: scripts/script.sh
```
#### 多个不同端口的主机
```diff
- name: 多主机
uses: appleboy/ssh-action@v1
- name: multiple host
uses: appleboy/ssh-action@v1.2.0
with:
- host: "foo.com"
+ host: "foo.com:1234,bar.com:5678"
- host: ”foo.com“
+ host: ”foo.com:1234,bar.com:5678“
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
script: |
@ -326,13 +241,13 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
ls -al
```
### 多主机同步执行
#### 主机同步执行
```diff
- name: 多主机
uses: appleboy/ssh-action@v1
- name: multiple host
uses: appleboy/ssh-action@v1.2.0
with:
host: "foo.com,bar.com"
host: ”foo.com,bar.com“
+ sync: true
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
@ -342,14 +257,14 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
ls -al
```
### 传递环境变量到 shell 脚本
#### 将环境变量传递到 Shell 脚本
```diff
- name: 传递环境变量
uses: appleboy/ssh-action@v1
- name: pass environment
uses: appleboy/ssh-action@v1.2.0
+ env:
+ FOO: "BAR"
+ BAR: "FOO"
+ FOO: ”BAR“
+ BAR: ”FOO“
+ SHA: ${{ github.sha }}
with:
host: ${{ secrets.HOST }}
@ -358,26 +273,53 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
port: ${{ secrets.PORT }}
+ envs: FOO,BAR,SHA
script: |
echo "I am $FOO"
echo "I am $BAR"
echo "sha: $SHA"
echo ”I am $FOO“
echo ”I am $BAR“
echo ”sha: $SHA“
```
> _`env` 对象中的所有环境变量必须为字符串。传递整数或其他类型可能导致意外结果。_
_在 `env` 对象中,您需要将每个环境变量作为字符串传递,传递 `Integer` 数据类型或任何其他类型可能会产生意外结果。_
---
#### 在第一次失败后停止脚本
## 🌐 代理与跳板机用法
> ex: missing `abc` folder
你可以通过代理(跳板机)连接到远程主机,适用于进阶网络拓扑。
```diff
- name: stop script if command error
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
+ script_stop: true
script: |
mkdir abc/def
ls -al
```
画面输出:
```sh
======CMD======
mkdir abc/def
ls -al
======END======
2019/11/21 01:16:21 Process exited with status 1
err: mkdir: cannot create directory abc/def: No such file or directory
##[error]Docker run failed with exit code 1
```
#### 如何使用 `ProxyCommand` 连接远程服务器?
```bash
+--------+ +----------+ +-----------+
| Laptop | <--> | Jumphost | <--> | FooServer |
+--------+ +----------+ +-----------+
+———+ +-———+ +————+
| Laptop | <—> | Jumphost | <—> | FooServer |
+———+ +-———+ +————+
```
示例 `~/.ssh/config`
在您的 `~/.ssh/config` 文件中,您会看到以下内容。
```bash
Host Jumphost
@ -393,11 +335,11 @@ Host FooServer
ProxyCommand ssh -q -W %h:%p Jumphost
```
**GitHub Actions YAML:**
#### 如何将其转换为 GitHubActions 的 YAML 格式?
```diff
- name: SSH 代理命令
uses: appleboy/ssh-action@v1
- name: ssh proxy command
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -412,17 +354,13 @@ Host FooServer
ls -al
```
---
#### 如何保护私钥?
## 🛡️ 安全最佳实践
### 保护你的私钥
密码短语会加密你的私钥,即使泄露也无法被攻击者直接利用。请务必妥善保管私钥。
密码短语通常用于加密私钥。这使得攻击者无法单独使用密钥文件。文件泄露可能来自备份或停用的硬件,黑客通常可以从受攻击系统中泄露文件。因此,保护私钥非常重要。
```diff
- name: SSH 密钥密码短语
uses: appleboy/ssh-action@v1
- name: ssh key passphrase
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -434,19 +372,21 @@ Host FooServer
ls -al
```
### 主机指纹验证
#### 使用主机指纹验证
验证 SSH 主机指纹有助于防止中间人攻击。获取主机指纹(将 `ed25519` 替换为你的密钥类型,`example.com` 替换为你的主机):
设置 SSH 主机指纹验证可以帮助防止中间人攻击。在设置之前,运行以下命令以获取 SSH 主机指纹。请记得将 `ed25519` 替换为您适当的密钥类型(`rsa``dsa`等),而 `example.com` 则替换为您的主机。
现代 OpenSSH 版本中需要提取的_默认密钥_类型是 `rsa`(从版本 5.1 开始)、`ecdsa`(从版本 6.0 开始)和 `ed25519`(从版本 6.7 开始)。
```sh
ssh example.com ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub | cut -d ' ' -f2
ssh example.com ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub | cut -d -f2
```
更新配置:
现在您可以调整您的配置:
```diff
- name: SSH 密钥密码短语
uses: appleboy/ssh-action@v1
- name: ssh key passphrase
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -458,33 +398,10 @@ ssh example.com ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub | cut -d ' '
ls -al
```
---
## 贡献
## 🚨 错误处理与疑难解答
我们非常希望您为 `appleboy/ssh-action` 做出贡献,欢迎提交请求!
### 常见问题
## 授权方式
#### 命令未找到npm 或其他命令)
如果遇到 "command not found" 错误,请参考 [此评论](https://github.com/appleboy/ssh-action/issues/31#issuecomment-1006565847) 了解交互式与非交互式 shell 的区别。
许多 Linux 发行版的 `/etc/bash.bashrc` 包含如下内容:
```sh
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
```
注释掉该行或使用命令的绝对路径。
---
## 🤝 贡献
欢迎贡献!请提交 Pull Request 改进 `appleboy/ssh-action`
---
## 📝 许可证
本项目采用 [MIT License](LICENSE) 授权。
本项目中的脚本和文档采用 [MIT](LICENSE) 许可证 发布。

View File

@ -1,247 +1,164 @@
# 🚀 GitHub Actions 的 SSH
# 🚀 用於 GitHub Actions 的 SSH
[English](./README.md) | 繁體中文 | [简体中文](./README.zh-cn.md)
## 目錄
- [🚀 GitHub Actions 的 SSH](#-github-actions-的-ssh)
- [目錄](#目錄)
- [📖 簡介](#-簡介)
- [🧩 核心概念與輸入參數](#-核心概念與輸入參數)
- [🔌 連線設定](#-連線設定)
- [🛠️ 指令設定](#-指令設定)
- [🌐 代理設定](#-代理設定)
- [⚡ 快速開始](#-快速開始)
- [🔑 SSH 金鑰設定與 OpenSSH 相容性](#-ssh-金鑰設定與-openssh-相容性)
- [設定 SSH 金鑰](#設定-ssh-金鑰)
- [產生 RSA 金鑰](#產生-rsa-金鑰)
- [產生 ED25519 金鑰](#產生-ed25519-金鑰)
- [OpenSSH 相容性](#openssh-相容性)
- [🛠️ 用法場景與進階範例](#-用法場景與進階範例)
- [使用密碼認證](#使用密碼認證)
- [使用私鑰認證](#使用私鑰認證)
- [多條指令](#多條指令)
- [從檔案執行指令](#從檔案執行指令)
- [多主機](#多主機)
- [多主機不同埠號](#多主機不同埠號)
- [多主機同步執行](#多主機同步執行)
- [傳遞環境變數到 shell 腳本](#傳遞環境變數到-shell-腳本)
- [🌐 代理與跳板機用法](#-代理與跳板機用法)
- [🛡️ 安全最佳實踐](#-安全最佳實踐)
- [保護你的私鑰](#保護你的私鑰)
- [主機指紋驗證](#主機指紋驗證)
- [🚨 錯誤處理與疑難排解](#-錯誤處理與疑難排解)
- [常見問題](#常見問題)
- [指令找不到npm 或其他指令)](#指令找不到npm-或其他指令)
- [🤝 貢獻](#-貢獻)
- [📝 授權](#-授權)
---
## 📖 簡介
**SSH for GitHub Actions** 是一個強大的 [GitHub Action](https://github.com/features/actions),可讓你在 CI/CD 工作流程中輕鬆且安全地執行遠端 SSH 指令。
本專案以 [Golang](https://go.dev) 和 [drone-ssh](https://github.com/appleboy/drone-ssh) 建立,支援多主機、代理、進階認證等多種 SSH 場景。
[GitHub Action](https://github.com/features/actions) for executing remote ssh commands.
![ssh workflow](./images/ssh-workflow.png)
[![testing main branch](https://github.com/appleboy/ssh-action/actions/workflows/main.yml/badge.svg)](https://github.com/appleboy/ssh-action/actions/workflows/main.yml)
[![Actions Status](https://github.com/appleboy/ssh-action/workflows/remote%20ssh%20command/badge.svg)](https://github.com/appleboy/ssh-action/actions)
---
**注意** 只支援在 **Linux** [docker](https://www.docker.com/) 容器上執行。
## 🧩 核心概念與輸入參
## 輸入變數
本 Action 提供彈性的 SSH 指令執行能力,並具備豐富的設定選項
更詳細的資訊,請參閱 [action.yml](./action.yml)。
完整參數請參閱 [action.yml](./action.yml)。
* `host` - SSH 主機
* `port` - SSH 連接埠,預設為 `22`
* `username` - SSH 使用者名稱
* `password` - SSH 密碼
* `passphrase` - 通常用於加密私鑰的 passphrase
* `sync` - 同步執行多個主機上的命令,預設為 false
* `timeout` - SSH 連接到遠端主機的超時時間,預設為 `30s`
* `command_timeout` - SSH 命令超時時間,預設為 10m
* `key` - SSH 私鑰的內容,例如 ~/.ssh/id_rsa 的原始內容,請記得包含 BEGIN 和 END 行
* `key_path` - SSH 私鑰的路徑
* `fingerprint` - 主機公鑰的 SHA256 指紋,預設為略過驗證
* `script` - 執行命令
* `script_file` - 執行命令的文件
* `script_stop` - 當出現第一個錯誤時停止執行命令
* `envs` - 傳遞環境變數到 shell script
* `debug` - 啟用偵錯模式
* `use_insecure_cipher` - 使用不安全的密碼ciphers進行加密參見 [#56](https://github.com/appleboy/ssh-action/issues/56)
* `cipher` - 允許使用的密碼ciphers演算法。如果未指定則使用適當的演算法
### 🔌 連線設定
SSH 代理設置:
這些參數用於控制如何連線到遠端主機。
* `proxy_host` - 代理主機
* `proxy_port` - 代理端口,預設為 `22`
* `proxy_username` - 代理使用者名稱
* `proxy_password` - 代理密碼
* `proxy_passphrase` - 密碼通常用於加密私有金鑰
* `proxy_timeout` - SSH 連線至代理主機的逾時時間,預設為 `30s`
* `proxy_key` - SSH 代理私有金鑰內容
* `proxy_key_path` - SSH 代理私有金鑰路徑
* `proxy_fingerprint` - 代理主機公鑰的 SHA256 指紋,預設為跳過驗證
* `proxy_use_insecure_cipher` - 使用不安全的加密方式,請參閱 [#56](https://github.com/appleboy/ssh-action/issues/56)
* `proxy_cipher` - 允許的加密算法。如果未指定,則使用合理的算法
| 參數 | 說明 | 預設值 |
| ------------------- | --------------------------------------------- | ------ |
| host | SSH 主機位址 | |
| port | SSH 埠號 | 22 |
| username | SSH 使用者名稱 | |
| password | SSH 密碼 | |
| protocol | SSH 協議版本(`tcp``tcp4``tcp6` | tcp |
| sync | 指定多個主機時同步執行 | false |
| timeout | SSH 連線主機的逾時時間 | 30s |
| key | SSH 私鑰內容(如 `~/.ssh/id_rsa` 的原始內容) | |
| key_path | SSH 私鑰路徑 | |
| passphrase | SSH 私鑰密碼 | |
| fingerprint | 主機公鑰的 SHA256 指紋 | |
| use_insecure_cipher | 允許額外(不安全)的加密演算法 | false |
| cipher | 允許的加密演算法,未指定時使用預設值 | |
## 使用方式
---
### 🛠️ 指令設定
這些參數用於控制在遠端主機上執行的指令及相關行為。
| 參數 | 說明 | 預設值 |
| --------------- | ----------------------------------------------------- | ------ |
| script | 遠端執行的指令 | |
| script_path | 包含要執行指令的檔案路徑 | |
| envs | 傳遞給 shell 腳本的環境變數 | |
| envs_format | 環境變數傳遞的彈性設定 | |
| allenvs | 傳遞所有帶 `GITHUB_``INPUT_` 前綴的環境變數到腳本 | false |
| command_timeout | SSH 指令執行逾時時間 | 10m |
| debug | 啟用除錯模式 | false |
| request_pty | 向伺服器請求偽終端 | false |
| curl_insecure | 允許 curl 連線無憑證的 SSL 網站 | false |
| version | drone-ssh 執行檔版本,未指定時使用最新版本 | |
---
### 🌐 代理設定
這些參數用於透過代理(跳板機)連線到目標主機。
| 參數 | 說明 | 預設值 |
| ------------------------- | ----------------------------------------- | ------ |
| proxy_host | SSH 代理主機 | |
| proxy_port | SSH 代理埠號 | 22 |
| proxy_username | SSH 代理使用者名稱 | |
| proxy_password | SSH 代理密碼 | |
| proxy_passphrase | SSH 代理私鑰密碼 | |
| proxy_protocol | SSH 代理協議版本(`tcp``tcp4``tcp6` | tcp |
| proxy_timeout | SSH 連線代理主機的逾時時間 | 30s |
| proxy_key | SSH 代理私鑰內容 | |
| proxy_key_path | SSH 代理私鑰路徑 | |
| proxy_fingerprint | 代理主機公鑰的 SHA256 指紋 | |
| proxy_cipher | 代理允許的加密演算法 | |
| proxy_use_insecure_cipher | 代理允許額外(不安全)的加密演算法 | false |
> **注意:** 如需實現已移除的 `script_stop` 功能,請在 shell 腳本最上方加上 `set -e`
---
## ⚡ 快速開始
只需簡單設定,即可在工作流程中執行遠端 SSH 指令:
執行遠端 SSH 命令
```yaml
name: Remote SSH Command
name: remote ssh command
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: 執行遠端 SSH 指令(密碼認證)
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: linuxserver.io
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: whoami
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: whoami
```
**輸出:**
畫面輸出
```sh
======CMD======
whoami
======END======
linuxserver.io
out: ***
===============================================
✅ Successfully executed commands to all hosts.
===============================================
```
---
### 設置 SSH 金鑰
## 🔑 SSH 金鑰設定與 OpenSSH 相容性
請在創建 SSH 金鑰並使用 SSH 金鑰時遵循以下步驟。最佳做法是在本地機器上創建 SSH 金鑰而不是遠端機器上。請使用 Github Secrets 中指定的用戶名登錄。生成 RSA 金鑰:
### 設定 SSH 金鑰
建議於本地端(非遠端伺服器)產生 SSH 金鑰。請以 GitHub Secrets 指定的使用者名稱登入並產生金鑰對:
#### 產生 RSA 金鑰
### 生成 RSA 金鑰
```bash
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
```
#### 產生 ED25519 金鑰
### 生成 ed25519 金鑰
```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
```
將新產生的公鑰加入伺服器的 authorized_keys。 [了解更多 authorized_keys](https://www.ssh.com/ssh/authorized_keys/)
將新生成的金鑰添加到已授權的金鑰中。詳細了解已授權的金鑰請點擊[此處](https://www.ssh.com/ssh/authorized_keys/).
### 將 RSA 金鑰添加到已授權金鑰中
```bash
# 加入 RSA 公鑰
cat .ssh/id_rsa.pub | ssh user@host 'cat >> .ssh/authorized_keys'
# 加入 ED25519 公鑰
cat .ssh/id_ed25519.pub | ssh user@host 'cat >> .ssh/authorized_keys'
cat .ssh/id_rsa.pub | ssh b@B 'cat >> .ssh/authorized_keys'
```
複製私鑰內容並貼到 GitHub Secrets。
### 將 ed25519 金鑰添加到已授權金鑰中
```bash
# macOS
pbcopy < ~/.ssh/id_rsa
# Ubuntu
xclip < ~/.ssh/id_rsa
cat .ssh/id_ed25519.pub | ssh b@B 'cat >> .ssh/authorized_keys'
```
> **提示:** 複製內容需包含 `-----BEGIN OPENSSH PRIVATE KEY-----``-----END OPENSSH PRIVATE KEY-----`(含)
複製私鑰內容,然後將其粘貼到 Github Secrets 中。
ED25519 同理:
### 複製 rsa 私鑰內容
```bash
# macOS
pbcopy < ~/.ssh/id_ed25519
# Ubuntu
xclip < ~/.ssh/id_ed25519
clip < ~/.ssh/id_rsa
```
更多資訊:[SSH 免密碼登入](http://www.linuxproblem.org/art_9.html)。
### 複製 ed25519 私鑰內容
> **注意:** 根據 SSH 版本,可能還需:
>
> - 將公鑰放入 `.ssh/authorized_keys2`
> - 設定 `.ssh` 權限為 700
> - 設定 `.ssh/authorized_keys2` 權限為 640
```bash
clip < ~/.ssh/id_ed25519
```
### OpenSSH 相容性
有關無需密碼登錄 SSH 的詳細信息,請[參見該網站](http://www.linuxproblem.org/art_9.html)。
若出現以下錯誤:
**來自讀者的注意事項** 根據您的 SSH 版本,您可能還需要進行以下更改:
* 將公鑰放在 `.ssh/authorized_keys2`
* 將 `.ssh` 的權限更改為700
* 將 `.ssh/authorized_keys2` 的權限更改為640
### 如果你使用的是 OpenSSH
如果您正在使用 OpenSSH並出現以下錯誤
```bash
ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey]
```
在 Ubuntu 20.04+,你可能需明確允許 `ssh-rsa` 演算法。請於 OpenSSH 設定檔(`/etc/ssh/sshd_config``/etc/ssh/sshd_config.d/` 下的 drop-in 檔案)加入:
請確保您所選擇的密鑰演算法得到支援。在 Ubuntu 20.04 或更高版本上,您必須明確允許使用 SSH-RSA 演算法。請在 OpenSSH 守護進程文件中添加以下行(它可以是 `/etc/ssh/sshd_config``/etc/ssh/sshd_config.d/` 中的一個附著文件)
```bash
CASignatureAlgorithms +ssh-rsa
```
直接使用預設支援的 ED25519 金鑰:
者,`Ed25519` 密鑰在 OpenSSH 中默認被接受。如果需要,您可以使用它來替代 RSA。
```bash
ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
```
---
### Example
## 🛠️ 用法場景與進階範例
本節涵蓋常見與進階用法,包括多主機、代理、環境變數傳遞等。
### 使用密碼認證
#### 使用密碼執行遠端 SSH 命令
```yaml
- name: 執行遠端 SSH 指令(密碼認證)
uses: appleboy/ssh-action@v1
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -250,11 +167,11 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
script: whoami
```
### 使用私鑰認證
#### 使用私鑰
```yaml
- name: 執行遠端 SSH 指令(私鑰認證)
uses: appleboy/ssh-action@v1
- name: executing remote ssh commands using ssh key
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -263,11 +180,11 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
script: whoami
```
### 多條指
#### 多個命
```yaml
- name: 多條指令
uses: appleboy/ssh-action@v1
- name: multiple command
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -280,24 +197,24 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
![result](./images/output-result.png)
### 從檔案執行指令
#### Commands from a file
```yaml
- name: 檔案指令
uses: appleboy/ssh-action@v1
- name: file commands
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
script_path: scripts/script.sh
script_path: scripts/script.sh
```
### 多主機
####主機
```diff
- name: 多主機
uses: appleboy/ssh-action@v1
- name: multiple host
uses: appleboy/ssh-action@v1.2.0
with:
- host: "foo.com"
+ host: "foo.com,bar.com"
@ -309,13 +226,11 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
ls -al
```
預設 `port``22`
### 多主機不同埠號
#### 多個不同端口的主機
```diff
- name: 多主機
uses: appleboy/ssh-action@v1
- name: multiple host
uses: appleboy/ssh-action@v1.2.0
with:
- host: "foo.com"
+ host: "foo.com:1234,bar.com:5678"
@ -326,11 +241,11 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
ls -al
```
### 多主機同步執行
#### 主機同步執行
```diff
- name: 多主機
uses: appleboy/ssh-action@v1
- name: multiple host
uses: appleboy/ssh-action@v1.2.0
with:
host: "foo.com,bar.com"
+ sync: true
@ -342,11 +257,11 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
ls -al
```
### 傳遞環境變數到 shell 腳本
#### 將環境變量傳遞到 Shell 腳本
```diff
- name: 傳遞環境變數
uses: appleboy/ssh-action@v1
- name: pass environment
uses: appleboy/ssh-action@v1.2.0
+ env:
+ FOO: "BAR"
+ BAR: "FOO"
@ -363,13 +278,40 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
echo "sha: $SHA"
```
> _`env` 物件中的所有環境變數必須為字串。傳遞整數或其他型別可能導致非預期結果。_
_在 `env` 對象中,您需要將每個環境變量作為字符串傳遞,傳遞 `Integer` 數據類型或任何其他類型可能會產生意外結果。_
---
#### 在第一次失敗後停止腳本
## 🌐 代理與跳板機用法
> ex: missing `abc` folder
你可以透過代理(跳板機)連線到遠端主機,適用於進階網路拓撲。
```diff
- name: stop script if command error
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
+ script_stop: true
script: |
mkdir abc/def
ls -al
```
畫面輸出:
```sh
======CMD======
mkdir abc/def
ls -al
======END======
2019/11/21 01:16:21 Process exited with status 1
err: mkdir: cannot create directory abc/def: No such file or directory
##[error]Docker run failed with exit code 1
```
#### 如何使用 `ProxyCommand` 連接遠程服務器?
```bash
+--------+ +----------+ +-----------+
@ -377,7 +319,7 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
+--------+ +----------+ +-----------+
```
範例 `~/.ssh/config`
在您的 `~/.ssh/config` 文件中,您會看到以下內容。
```bash
Host Jumphost
@ -393,11 +335,11 @@ Host FooServer
ProxyCommand ssh -q -W %h:%p Jumphost
```
**GitHub Actions YAML:**
#### 如何將其轉換為 GitHubActions 的 YAML 格式?
```diff
- name: SSH 代理指令
uses: appleboy/ssh-action@v1
- name: ssh proxy command
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -412,17 +354,13 @@ Host FooServer
ls -al
```
---
#### 如何保護私鑰?
## 🛡️ 安全最佳實踐
### 保護你的私鑰
密碼短語會加密你的私鑰,即使外洩也無法被攻擊者直接利用。請務必妥善保管私鑰。
密碼短語通常用於加密私鑰。這使得攻擊者無法單獨使用密鑰文件。文件泄露可能來自備份或停用的硬件,黑客通常可以從受攻擊系統中洩露文件。因此,保護私鑰非常重要。
```diff
- name: SSH 私鑰密碼
uses: appleboy/ssh-action@v1
- name: ssh key passphrase
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -434,19 +372,21 @@ Host FooServer
ls -al
```
### 主機指紋驗證
#### 使用主機指紋驗證
驗證 SSH 主機指紋有助於防止中間人攻擊。取得主機指紋(將 `ed25519` 換成你的金鑰型別,`example.com` 換成你的主機):
設置 SSH 主機指紋驗證可以幫助防止中間人攻擊。在設置之前,運行以下命令以獲取 SSH 主機指紋。請記得將 `ed25519` 替換為您的適當金鑰類型(`rsa``dsa`等),而 `example.com` 則替換為您的主機。
現代 OpenSSH 版本中需要提取的_默認金鑰_類型是 `rsa`(從版本 5.1 開始)、`ecdsa`(從版本 6.0 開始)和 `ed25519`(從版本 6.7 開始)。
```sh
ssh example.com ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub | cut -d ' ' -f2
```
更新設定
現在您可以調整您的配置
```diff
- name: SSH 私鑰密碼
uses: appleboy/ssh-action@v1
- name: ssh key passphrase
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
@ -458,33 +398,10 @@ ssh example.com ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub | cut -d ' '
ls -al
```
---
## 貢獻
## 🚨 錯誤處理與疑難排解
我們非常希望您為 `appleboy/ssh-action` 做出貢獻,歡迎提交請求!
### 常見問題
## 授權方式
#### 指令找不到npm 或其他指令)
若遇到 "command not found" 錯誤,請參考 [此討論](https://github.com/appleboy/ssh-action/issues/31#issuecomment-1006565847) 了解互動式與非互動式 shell 差異。
許多 Linux 發行版的 `/etc/bash.bashrc` 包含如下內容:
```sh
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
```
請將該行註解掉或使用指令的絕對路徑。
---
## 🤝 貢獻
歡迎貢獻!請提交 Pull Request 改善 `appleboy/ssh-action`
---
## 📝 授權
本專案採用 [MIT License](LICENSE) 授權。
本項目中的腳本和文檔采用 [MIT](LICENSE) 許可證 發布。

View File

@ -3,92 +3,80 @@ description: "Executing remote ssh commands"
author: "Bo-Yi Wu"
inputs:
host:
description: "SSH host address or IP to connect to."
description: "SSH host address."
port:
description: "SSH port number for the connection."
description: "SSH port number."
default: "22"
passphrase:
description: "Passphrase to decrypt the SSH private key if protected."
description: "Passphrase for the SSH key."
username:
description: "SSH username for authentication on the remote server."
description: "SSH username."
password:
description: "SSH password for authentication (use secrets for sensitive data)."
description: "SSH password."
protocol:
description: 'IP protocol version to use. Options: "tcp" (default), "tcp4" (IPv4 only), or "tcp6" (IPv6 only).'
description: 'The IP protocol to use. Valid values are "tcp". "tcp4" or "tcp6". Default to tcp.'
default: "tcp"
sync:
description: "When true, executes commands synchronously across multiple hosts (one after another)."
description: "Enable synchronous execution if multiple hosts are involved."
use_insecure_cipher:
description: "Enable additional legacy ciphers that might be less secure but more compatible with older systems."
description: "Include more ciphers by using insecure ciphers."
cipher:
description: "Specify custom cipher algorithms for encryption. Leave empty to use secure defaults."
description: "Allowed cipher algorithms. If unspecified, a sensible default is used."
timeout:
description: "Maximum time to wait when establishing the SSH connection, e.g., '30s', '1m'."
description: "Timeout duration for establishing SSH connection to the host."
default: "30s"
command_timeout:
description: "Maximum execution time for the remote commands before terminating, e.g., '10m', '1h'."
description: "Timeout duration for SSH commands execution."
default: "10m"
key:
description: "Raw content of the SSH private key for authentication (use secrets for sensitive data)."
description: "Content of the SSH private key. For example, the raw content of ~/.ssh/id_rsa."
key_path:
description: "Path to the SSH private key file on the runner."
description: "Path to the SSH private key file."
fingerprint:
description: "SHA256 fingerprint of the host public key for verification to prevent MITM attacks."
description: "SHA256 fingerprint of the host public key."
proxy_host:
description: "Proxy server hostname or IP if connecting through an SSH jump host."
description: "SSH proxy host address."
proxy_port:
description: "SSH port number for the proxy connection."
description: "SSH proxy port number."
default: "22"
proxy_username:
description: "Username for authentication on the proxy server."
description: "SSH proxy username."
proxy_password:
description: "Password for authentication on the proxy server (use secrets for sensitive data)."
description: "SSH proxy password."
proxy_protocol:
description: 'IP protocol version for proxy. Options: "tcp" (default), "tcp4" (IPv4 only), or "tcp6" (IPv6 only).'
description: 'The IP protocol to use. Valid values are "tcp". "tcp4" or "tcp6". Default to tcp.'
default: "tcp"
proxy_passphrase:
description: "Passphrase to decrypt the proxy SSH private key if protected."
description: "SSH proxy key passphrase."
proxy_timeout:
description: "Maximum time to wait when establishing the proxy SSH connection, e.g., '30s', '1m'."
description: "Timeout duration for establishing SSH connection to the proxy host."
default: "30s"
proxy_key:
description: "Raw content of the SSH proxy private key for authentication (use secrets for sensitive data)."
description: "Content of the SSH proxy private key. For example, the raw content of ~/.ssh/id_rsa."
proxy_key_path:
description: "Path to the SSH proxy private key file on the runner."
description: "Path to the SSH proxy private key file."
proxy_fingerprint:
description: "SHA256 fingerprint of the proxy host public key for verification."
description: "SHA256 fingerprint of the proxy host public key."
proxy_cipher:
description: "Specify custom cipher algorithms for proxy connection encryption."
description: "Allowed cipher algorithms for the proxy. If unspecified, a sensible default is used."
proxy_use_insecure_cipher:
description: "Enable additional legacy ciphers for proxy connections (less secure but more compatible)."
description: "Include more ciphers for the proxy by using insecure ciphers."
script:
description: "Commands to execute on the remote server (inline script string)."
description: "Commands to be executed."
script_path:
description: "Path to a local file containing commands to execute on the remote server."
description: "Path to the file containing commands to be executed."
script_stop:
description: "Stop the script after the first failure."
envs:
description: "Environment variables to expose to the remote script, format: key=value,key2=value2."
description: "Environment variables to be passed to the shell script."
envs_format:
description: "Format specification for environment variable transfer (for advanced usage)."
description: "Flexible configuration for environment value transfer."
debug:
description: "Set to true to enable verbose logging for troubleshooting connection issues."
description: "Enable debug mode."
allenvs:
description: "When true, passes all GitHub Actions environment variables to the remote script."
description: "pass all environment variable to shell script."
request_pty:
description: "Request a pseudo-terminal from the server (required for interactive commands or sudo)."
curl_insecure:
description: "When true, uses the --insecure option with curl for insecure downloads."
default: "false"
capture_stdout:
description: "When true, captures and returns standard output from the commands as action output."
default: "false"
version:
description: |
The version of drone-ssh to use.
outputs:
stdout:
description: "Standard output of the executed commands when capture_stdout is enabled."
value: ${{ steps.entrypoint.outputs.stdout }}
description: "Request a pseudo-terminal from the server."
runs:
using: "composite"
@ -98,8 +86,7 @@ runs:
shell: bash
env:
GITHUB_ACTION_PATH: ${{ github.action_path }}
- id: entrypoint
name: Run entrypoint.sh
- name: Run entrypoint.sh
run: entrypoint.sh
shell: bash
env:
@ -126,6 +113,7 @@ runs:
INPUT_COMMAND_TIMEOUT: ${{ inputs.command_timeout }}
INPUT_SCRIPT: ${{ inputs.script }}
INPUT_SCRIPT_FILE: ${{ inputs.script_path }}
INPUT_SCRIPT_STOP: ${{ inputs.script_stop }}
INPUT_ENVS: ${{ inputs.envs }}
INPUT_ENVS_FORMAT: ${{ inputs.envs_format }}
INPUT_DEBUG: ${{ inputs.debug }}
@ -136,9 +124,6 @@ runs:
INPUT_PROXY_USE_INSECURE_CIPHER: ${{ inputs.proxy_use_insecure_cipher }}
INPUT_PROXY_CIPHER: ${{ inputs.proxy_cipher }}
INPUT_SYNC: ${{ inputs.sync }}
INPUT_CAPTURE_STDOUT: ${{ inputs.capture_stdout }}
INPUT_CURL_INSECURE: ${{ inputs.curl_insecure }}
DRONE_SSH_VERSION: ${{ inputs.version }}
branding:
icon: "terminal"

View File

@ -1,56 +1,70 @@
#!/usr/bin/env bash
set -euo pipefail
set -o errexit
set -o nounset
set -o pipefail
export GITHUB="true"
GITHUB_ACTION_PATH="${GITHUB_ACTION_PATH%/}"
DRONE_SSH_RELEASE_URL="${DRONE_SSH_RELEASE_URL:-https://github.com/appleboy/drone-ssh/releases/download}"
DRONE_SSH_VERSION="${DRONE_SSH_VERSION:-1.8.1}"
function log_error() {
echo "$1" >&2
exit "$2"
}
DRONE_SSH_VERSION="${DRONE_SSH_VERSION:-1.8.0}"
function detect_client_info() {
CLIENT_PLATFORM="${SSH_CLIENT_OS:-$(uname -s | tr '[:upper:]' '[:lower:]')}"
CLIENT_ARCH="${SSH_CLIENT_ARCH:-$(uname -m)}"
if [ -n "${SSH_CLIENT_OS-}" ]; then
CLIENT_PLATFORM="${SSH_CLIENT_OS}"
else
local kernel
kernel="$(uname -s)"
case "${kernel}" in
Darwin)
CLIENT_PLATFORM="darwin"
;;
Linux)
CLIENT_PLATFORM="linux"
;;
Windows)
CLIENT_PLATFORM="windows"
;;
*)
echo "Unknown, unsupported platform: ${kernel}." >&2
echo "Supported platforms: Linux, Darwin and Windows." >&2
echo "Bailing out." >&2
exit 2
;;
esac
fi
case "${CLIENT_PLATFORM}" in
darwin | linux | windows) ;;
*) log_error "Unknown or unsupported platform: ${CLIENT_PLATFORM}. Supported platforms are Linux, Darwin, and Windows." 2 ;;
esac
case "${CLIENT_ARCH}" in
x86_64* | i?86_64* | amd64*) CLIENT_ARCH="amd64" ;;
aarch64* | arm64*) CLIENT_ARCH="arm64" ;;
*) log_error "Unknown or unsupported architecture: ${CLIENT_ARCH}. Supported architectures are x86_64, i686, and arm64." 3 ;;
esac
if [ -n "${SSH_CLIENT_ARCH-}" ]; then
CLIENT_ARCH="${SSH_CLIENT_ARCH}"
else
local machine
machine="$(uname -m)"
case "${machine}" in
x86_64* | i?86_64* | amd64*)
CLIENT_ARCH="amd64"
;;
aarch64* | arm64*)
CLIENT_ARCH="arm64"
;;
*)
echo "Unknown, unsupported architecture (${machine})." >&2
echo "Supported architectures x86_64, i686, arm64." >&2
echo "Bailing out." >&2
exit 3
;;
esac
fi
}
detect_client_info
DOWNLOAD_URL_PREFIX="${DRONE_SSH_RELEASE_URL}/v${DRONE_SSH_VERSION}"
CLIENT_BINARY="drone-ssh-${DRONE_SSH_VERSION}-${CLIENT_PLATFORM}-${CLIENT_ARCH}"
TARGET="${GITHUB_ACTION_PATH}/${CLIENT_BINARY}"
echo "Downloading ${CLIENT_BINARY} from ${DOWNLOAD_URL_PREFIX}"
INSECURE_OPTION=""
if [[ "${INPUT_CURL_INSECURE}" == 'true' ]]; then
INSECURE_OPTION="--insecure"
fi
curl -fsSL --retry 5 --keepalive-time 2 ${INSECURE_OPTION} "${DOWNLOAD_URL_PREFIX}/${CLIENT_BINARY}" -o "${TARGET}"
chmod +x "${TARGET}"
echo "======= CLI Version Information ======="
"${TARGET}" --version
echo "======================================="
if [[ "${INPUT_CAPTURE_STDOUT}" == 'true' ]]; then
{
echo 'stdout<<EOF'
"${TARGET}" "$@" | tee -a "${GITHUB_OUTPUT}"
echo 'EOF'
} >>"${GITHUB_OUTPUT}"
else
"${TARGET}" "$@"
fi
echo "Will download ${CLIENT_BINARY} from ${DOWNLOAD_URL_PREFIX}"
curl -fsSL --retry 5 --keepalive-time 2 "${DOWNLOAD_URL_PREFIX}/${CLIENT_BINARY}" -o ${TARGET}
chmod +x ${TARGET}
echo "======= CLI Version ======="
sh -c "${TARGET} --version" # print version
echo "==========================="
sh -c "${TARGET} $*" # run the command