Compare commits

..

17 Commits

Author SHA1 Message Date
Bo-Yi Wu c4f70287fc chore(ci): improve repository maintenance configuration
- Enable weekly Dependabot updates for GitHub Actions dependencies
- Consolidate duplicate Trivy scans into a single SARIF scan with exit-code gate
- Ignore macOS metadata and locally downloaded drone-ssh binaries
- Add feature request issue template and pull request checklist template

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 20:07:09 +08:00
Bo-Yi Wu 6f15e4fabc chore(deps): bump default drone-ssh version to 1.8.4
- Update the default binary version from 1.8.2 to 1.8.4
- Sync the version reference in the development guide

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 20:06:59 +08:00
Bo-Yi Wu 7e02feefeb fix: append .exe suffix to Windows binary download filename (#418)
* fix: append .exe suffix to Windows binary download filename

The drone-ssh release publishes Windows assets with an .exe suffix
(e.g. drone-ssh-1.8.2-windows-amd64.exe), but entrypoint.sh built the
download filename without it, so every Windows runner failed with a 404
at the download step (ERR_DOWNLOAD_FAILED).

Append .exe when the detected platform is windows. This also keeps the
checksums.txt lookup working on Windows since entries match the exact
asset name. Add a windows-latest CI job that exercises the download,
checksum verification, and binary execution path.

Fixes #417

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: detect Git Bash/MSYS/Cygwin uname output as windows platform

On windows-latest runners, bash steps run under Git Bash where
uname -s reports MINGW64_NT-10.0-<build>, so platform detection
rejected Windows runners with ERR_UNKNOWN_PLATFORM before the
download step was even reached. Map mingw*/msys*/cygwin* to windows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 11:59:12 +08:00
Andrea Lorenzetti ca58dd0ce3 feat(security): verify drone-ssh binary checksum after download (#410)
* Verify drone-ssh binary checksum after download

Download checksums.txt from the drone-ssh release alongside the binary and verify it. Protects against tampered or corrupted release artifacts.

* refactor: make checksum verification portable across minimal containers

- Detect shasum (Perl) or sha256sum (coreutils/busybox) and fall back
  gracefully; warn and skip verification only when neither tool exists,
  so container jobs without perl are not broken
- Look up the exact checksums.txt entry for the target binary and
  compare hashes directly, avoiding the --ignore-missing flag that
  busybox sha256sum does not support
- Fail closed when checksums.txt has no entry for the binary
- Remove checksums.txt after successful verification

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 11:21:31 +08:00
Bo-Yi Wu b838bc2f27 chore(ci): bump actions/setup-go from v6 to v7
- Update actions/setup-go from v6 to v7 in workflow files
2026-07-18 09:58:00 +08:00
Bo-Yi Wu 068c0c7a4e chore(ci): update GitHub Actions dependencies to latest versions
- Bump actions/checkout from v6 to v7
2026-06-23 20:07:08 +08:00
Bo-Yi Wu 1530429296 chore(deps): use v-prefixed tag for aquasecurity/trivy-action
- Pin Trivy action to v0.36.0 tag format
2026-04-29 22:18:55 +08:00
Bo-Yi Wu 81fc8ccc33 chore(deps): bump aquasecurity/trivy-action from 0.35.0 to 0.36.0
- Update Trivy action version in security scan workflow
2026-04-29 22:10:41 +08:00
Bo-Yi Wu 90162d1e43 chore(ci): update GitHub Actions dependencies to latest versions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 12:06:40 +08:00
dependabot[bot] 8743aa11bf chore(deps): bump aquasecurity/trivy-action in /.github/workflows (#405)
Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.33.1 to 0.34.0.
- [Release notes](https://github.com/aquasecurity/trivy-action/releases)
- [Commits](https://github.com/aquasecurity/trivy-action/compare/0.33.1...0.34.0)

---
updated-dependencies:
- dependency-name: aquasecurity/trivy-action
  dependency-version: 0.34.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-21 22:52:02 +08:00
Bo-Yi Wu 0ff4204d59 refactor: streamline output handling for GITHUB_OUTPUT in workflows (#404)
* refactor: streamline output handling for GITHUB_OUTPUT in workflows

- Write the stdout<<EOF and EOF markers directly to GITHUB_OUTPUT instead of using a group command
- Simplify the process for capturing and appending command output to GITHUB_OUTPUT

fix #403
fix #397

Signed-off-by: appleboy <appleboy.tw@gmail.com>

* test: enhance stdout capture and verification in tests

- Add a check to ensure captured stdout is not empty
- Add steps to capture and verify multiline stdout output
- Add verification that specific lines and the username are present in captured output
- Add steps to handle and verify stdout containing special characters and file paths

Signed-off-by: appleboy <appleboy.tw@gmail.com>

* ci: enforce unique occurrence of lines in multiline output validation

- Add a step to verify that lines "Line 1", "Line 2", and "Line 3" each appear exactly once in the multiline output
- Fail the workflow if any line is missing or duplicated
- Confirm successful validation with a message when no duplicates are found

Signed-off-by: appleboy <appleboy.tw@gmail.com>

---------

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2026-01-28 10:18:18 +08:00
appleboy 8e460a28f2 docs: improve documentation table formatting for output descriptions
- Adjust table formatting for improved output descriptions alignment in the documentation

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2026-01-28 09:29:11 +08:00
appleboy 23bd972bfc docs: update README and assets for new SSH agent workflow
- Replace the ssh workflow image with a new ssh agent image across all language versions of the README
- Add a link to related presentation slides in the README files
- Remove the old ssh-workflow.png image and add a new ssh-agent.png image to the repository

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-12-22 21:56:57 +08:00
appleboy 823bd89e13 ci: trigger GitHub Actions workflows only on version tags
- Restrict the GitHub Actions workflow to trigger only on tags matching the pattern vMAJOR.MINOR.PATCH

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-11-28 23:09:09 +08:00
appleboy f6208e096d docs: document and demonstrate capturing and using command output
- Add documentation outlining usage of output variables, specifically the captured command standard output
- Introduce the `capture_stdout` parameter to command options tables
- Update Quick Start examples to use a secret for the username, and clarify output handling
- Provide example workflows showing how to capture and use command output in subsequent steps
- Change code block language from bash to text for SSH configuration snippets

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-11-28 23:07:52 +08:00
appleboy 652a0bee3c docs: update CI documentation and workflow references
- Clarify that the main CI workflow tests the local action, not just Docker containers
- Add references to new workflows for testing the published tag and for automated security scanning
- Explain the naming difference for the `INPUT_SCRIPT_FILE` environment variable
- Update documentation to reflect default drone-ssh version change from 1.8.1 to 1.8.2

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-11-28 22:55:57 +08:00
appleboy 4e3535e14e chore: bump default DRONE_SSH_VERSION to 1.8.2
- Update the default DRONE_SSH_VERSION from 1.8.1 to 1.8.2

Signed-off-by: appleboy <appleboy.tw@gmail.com>
2025-11-28 22:51:52 +08:00
15 changed files with 374 additions and 59 deletions
+36
View File
@@ -0,0 +1,36 @@
---
name: Feature request
about: Suggest an idea for this action
title: ""
labels: enhancement
assignees: appleboy
---
## Is your feature request related to a problem?
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
## Describe the solution you'd like
A clear and concise description of what you want to happen.
## Example Yaml Config
If applicable, show how you would expect to use the new feature:
```yaml
- name: executing remote ssh commands
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
script: whoami
```
## Additional context
Add any other context about the feature request here. Note that SSH connection
behavior (ciphers, timeouts, proxy, etc.) is implemented in
[drone-ssh](https://github.com/appleboy/drone-ssh) - feature requests for
connection behavior may belong there.
+14
View File
@@ -0,0 +1,14 @@
## Description
Please describe what this PR changes and why.
## Related Issues
Fixes #
## Checklist
- [ ] Tests pass (`.github/workflows/main.yml` scenarios cover this change)
- [ ] `action.yml` input descriptions updated (if adding/changing parameters)
- [ ] README.md parameter tables updated (if adding/changing parameters)
- [ ] Chinese translations updated (README.zh-cn.md, README.zh-tw.md)
+10
View File
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
commit-message:
prefix: "chore(ci)"
+4 -4
View File
@@ -3,7 +3,7 @@ name: Goreleaser
on:
push:
tags:
- "*"
- "v[0-9]+.[0-9]+.[0-9]+"
permissions:
contents: write
@@ -13,17 +13,17 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup go
uses: actions/setup-go@v5
uses: actions/setup-go@v7
with:
go-version: "^1"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
uses: goreleaser/goreleaser-action@v7
with:
# either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser
+122 -12
View File
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: create new ssh server
run: |
@@ -70,7 +70,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: add public key to env
run: |
@@ -142,7 +142,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: add public key to env
run: |
@@ -223,7 +223,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: add public key to env
run: |
@@ -297,7 +297,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: add public key to env
run: |
@@ -351,7 +351,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: add public key to env
run: |
@@ -496,10 +496,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Set up WARP
uses: fscarmen/warp-on-actions@v1.1
uses: fscarmen/warp-on-actions@v1.4
with:
stack: dual
@@ -525,7 +525,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Set Environment Variables
run: |
@@ -569,7 +569,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: create new ssh server
run: |
@@ -609,12 +609,108 @@ jobs:
- name: check stdout
run: |
echo "stdout: ${{ steps.stdout.outputs.stdout }}"
if [ -z "${{ steps.stdout.outputs.stdout }}" ]; then
echo "Error: stdout is empty"
exit 1
fi
- id: stdout-multiline
name: capture multiline output
uses: ./
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
password: password
port: 2222
capture_stdout: true
script: |
#!/usr/bin/env bash
set -e
echo "Line 1"
echo "Line 2"
echo "Line 3"
whoami
pwd
- name: check multiline output
run: |
echo "stdout: ${{ steps.stdout-multiline.outputs.stdout }}"
# Check if all lines are present
if ! echo "${{ steps.stdout-multiline.outputs.stdout }}" | grep -q "Line 1"; then
echo "Error: 'Line 1' not found in output"
exit 1
fi
if ! echo "${{ steps.stdout-multiline.outputs.stdout }}" | grep -q "Line 2"; then
echo "Error: 'Line 2' not found in output"
exit 1
fi
if ! echo "${{ steps.stdout-multiline.outputs.stdout }}" | grep -q "Line 3"; then
echo "Error: 'Line 3' not found in output"
exit 1
fi
if ! echo "${{ steps.stdout-multiline.outputs.stdout }}" | grep -q "linuxserver.io"; then
echo "Error: username not found in output"
exit 1
fi
# Check for duplicates - each unique line should appear exactly once
OUTPUT="${{ steps.stdout-multiline.outputs.stdout }}"
LINE1_COUNT=$(echo "$OUTPUT" | grep -c "^Line 1$" || true)
LINE2_COUNT=$(echo "$OUTPUT" | grep -c "^Line 2$" || true)
LINE3_COUNT=$(echo "$OUTPUT" | grep -c "^Line 3$" || true)
echo "Line 1 count: $LINE1_COUNT"
echo "Line 2 count: $LINE2_COUNT"
echo "Line 3 count: $LINE3_COUNT"
if [ "$LINE1_COUNT" -ne 1 ]; then
echo "Error: 'Line 1' appears $LINE1_COUNT times (expected 1)"
exit 1
fi
if [ "$LINE2_COUNT" -ne 1 ]; then
echo "Error: 'Line 2' appears $LINE2_COUNT times (expected 1)"
exit 1
fi
if [ "$LINE3_COUNT" -ne 1 ]; then
echo "Error: 'Line 3' appears $LINE3_COUNT times (expected 1)"
exit 1
fi
echo "✓ No duplicate lines detected"
- id: stdout-with-special-chars
name: capture output with special characters
uses: ./
with:
host: ${{ env.REMOTE_HOST }}
username: linuxserver.io
password: password
port: 2222
capture_stdout: true
script: |
#!/usr/bin/env bash
set -e
echo "Test with special chars: @#$%^&*()"
echo "Path: /home/user/test"
echo "JSON: {\"key\": \"value\"}"
- name: check special characters output
run: |
echo "stdout: ${{ steps.stdout-with-special-chars.outputs.stdout }}"
if ! echo "${{ steps.stdout-with-special-chars.outputs.stdout }}" | grep -q "special chars"; then
echo "Error: special characters test failed"
exit 1
fi
if ! echo "${{ steps.stdout-with-special-chars.outputs.stdout }}" | grep -q "/home/user/test"; then
echo "Error: path not found in output"
exit 1
fi
testing-script-stop:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: create new ssh server
run: |
@@ -697,11 +793,25 @@ jobs:
echo "Output contains 'True'"
fi
windows-binary-download:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v7
- name: test binary download and checksum verification
shell: bash
run: |
export GITHUB_ACTION_PATH="$PWD"
export INPUT_CURL_INSECURE=false
export INPUT_CAPTURE_STDOUT=false
./entrypoint.sh --help
testing-script-error:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: create new ssh server
run: |
+6 -6
View File
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: create new ssh server
run: |
@@ -55,7 +55,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: add public key to env
run: |
@@ -127,7 +127,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: add public key to env
run: |
@@ -208,7 +208,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: add public key to env
run: |
@@ -282,7 +282,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: add public key to env
run: |
@@ -336,7 +336,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: add public key to env
run: |
+4 -13
View File
@@ -22,10 +22,10 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Run Trivy vulnerability scanner in repo mode
uses: aquasecurity/trivy-action@0.33.1
uses: aquasecurity/trivy-action@v0.36.0
with:
scan-type: 'fs'
scan-ref: '.'
@@ -33,19 +33,10 @@ jobs:
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH,MEDIUM'
exit-code: '1'
- name: Upload Trivy results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: 'trivy-results.sarif'
- name: Run Trivy vulnerability scanner (table format)
uses: aquasecurity/trivy-action@0.33.1
with:
scan-type: 'fs'
scan-ref: '.'
scanners: 'vuln,secret,misconfig'
format: 'table'
severity: 'CRITICAL,HIGH,MEDIUM'
exit-code: '1'
+6
View File
@@ -0,0 +1,6 @@
# macOS
.DS_Store
# drone-ssh binaries downloaded by entrypoint.sh during local testing
drone-ssh-*
checksums.txt
+5 -3
View File
@@ -37,7 +37,9 @@ This is a GitHub Action for executing remote SSH commands. Built using a composi
- `action.yml` - Action metadata and input/output definitions
- `entrypoint.sh` - Platform detection, binary download, and execution
- `testdata/` - Test scripts and SSH keys for CI workflows
- `.github/workflows/main.yml` - Comprehensive test suite using Docker containers
- `.github/workflows/main.yml` - Comprehensive test suite using Docker containers (tests `./` local action)
- `.github/workflows/stable.yml` - Tests against published `appleboy/ssh-action@v1` tag
- `.github/workflows/trivy-scan.yml` - Automated security scanning for vulnerabilities and misconfigurations
## Testing
@@ -92,7 +94,7 @@ export GITHUB_ACTION_PATH="$(pwd)"
Users can provide scripts in two ways:
- `script`: Inline commands (via `INPUT_SCRIPT`)
- `script_path`: Path to a file in the repository (via `INPUT_SCRIPT_FILE`)
- `script_path`: Path to a file in the repository (maps to `INPUT_SCRIPT_FILE` env var - note the naming difference)
### Error Handling
@@ -160,7 +162,7 @@ Or use ED25519 keys instead (preferred).
The action pins to specific drone-ssh versions via:
- Default: `DRONE_SSH_VERSION="1.8.1"` in `entrypoint.sh`
- Default: `DRONE_SSH_VERSION="1.8.4"` in `entrypoint.sh`
- Override: Users can specify `version` input parameter
Update the default version when new drone-ssh releases are available.
+42 -5
View File
@@ -11,6 +11,7 @@ English | [繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md)
- [🔌 Connection Settings](#-connection-settings)
- [🛠️ SSH Command Settings](#-ssh-command-settings)
- [🌐 Proxy Settings](#-proxy-settings)
- [📤 Output Variables](#-output-variables)
- [⚡ Quick Start](#-quick-start)
- [🔑 SSH Key Setup \& OpenSSH Compatibility](#-ssh-key-setup--openssh-compatibility)
- [Setting Up SSH Keys](#setting-up-ssh-keys)
@@ -26,6 +27,7 @@ English | [繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md)
- [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)
- [Capturing command output](#capturing-command-output)
- [🌐 Proxy \& Jump Host Usage](#-proxy--jump-host-usage)
- [🛡️ Security Best Practices](#-security-best-practices)
- [Protecting Your Private Key](#protecting-your-private-key)
@@ -43,11 +45,13 @@ English | [繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md)
**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.
![ssh workflow](./images/ssh-workflow.png)
![ssh workflow](./images/ssh-agent.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)
[![Trivy Security Scan](https://github.com/appleboy/ssh-action/actions/workflows/trivy-scan.yml/badge.svg)](https://github.com/appleboy/ssh-action/actions/workflows/trivy-scan.yml)
**Slides:** [SSH for GitHub Actions](https://speakerdeck.com/appleboy/ssh-for-github-actions)
---
## 🧩 Core Concepts & Input Parameters
@@ -93,6 +97,7 @@ These parameters control the commands executed on the remote host and related be
| 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 |
| capture_stdout | Capture standard output from commands as action output | false |
| version | drone-ssh binary version. If not specified, the latest version will be used. | |
---
@@ -120,6 +125,16 @@ These parameters control the use of a proxy (jump host) for connecting to your t
---
## 📤 Output Variables
This action provides the following outputs that you can use in subsequent steps:
| Output | Description |
| ------ | -------------------------------------------------------------------------- |
| stdout | Standard output of the executed commands (requires `capture_stdout: true`) |
---
## ⚡ Quick Start
Run remote SSH commands in your workflow with minimal configuration:
@@ -136,7 +151,7 @@ jobs:
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: linuxserver.io
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: whoami
@@ -148,7 +163,7 @@ jobs:
======CMD======
whoami
======END======
linuxserver.io
out: your_username
===============================================
✅ Successfully executed commands to all hosts.
===============================================
@@ -222,7 +237,7 @@ ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publ
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/`):
```bash
```text
CASignatureAlgorithms +ssh-rsa
```
@@ -366,6 +381,28 @@ Default `port` is `22`.
> _All environment variables in the `env` object must be strings. Using integers or other types may cause unexpected results._
### Capturing command output
You can capture the standard output of remote commands and use it in subsequent steps:
```yaml
- name: Execute and capture output
id: ssh
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
capture_stdout: true
script: |
echo "Hello World"
hostname
- name: Use captured output
run: echo "SSH output was ${{ steps.ssh.outputs.stdout }}"
```
---
## 🌐 Proxy & Jump Host Usage
@@ -380,7 +417,7 @@ You can connect to remote hosts via a proxy (jump host) for advanced network top
Example `~/.ssh/config`:
```bash
```text
Host Jumphost
HostName Jumphost
User ubuntu
+42 -5
View File
@@ -11,6 +11,7 @@
- [🔌 连接设置](#-连接设置)
- [🛠️ 指令设置](#-指令设置)
- [🌐 代理设置](#-代理设置)
- [📤 输出变量](#-输出变量)
- [⚡ 快速开始](#-快速开始)
- [🔑 SSH 密钥配置与 OpenSSH 兼容性](#-ssh-密钥配置与-openssh-兼容性)
- [配置 SSH 密钥](#配置-ssh-密钥)
@@ -26,6 +27,7 @@
- [多主机不同端口](#多主机不同端口)
- [多主机同步执行](#多主机同步执行)
- [传递环境变量到 shell 脚本](#传递环境变量到-shell-脚本)
- [捕获命令输出](#捕获命令输出)
- [🌐 代理与跳板机用法](#-代理与跳板机用法)
- [🛡️ 安全最佳实践](#-安全最佳实践)
- [保护你的私钥](#保护你的私钥)
@@ -43,11 +45,13 @@
**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 场景。
![ssh workflow](./images/ssh-workflow.png)
![ssh workflow](./images/ssh-agent.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)
[![Trivy Security Scan](https://github.com/appleboy/ssh-action/actions/workflows/trivy-scan.yml/badge.svg)](https://github.com/appleboy/ssh-action/actions/workflows/trivy-scan.yml)
**幻灯片:** [SSH for GitHub Actions](https://speakerdeck.com/appleboy/ssh-for-github-actions)
---
## 🧩 核心概念与输入参数
@@ -93,6 +97,7 @@
| debug | 启用调试模式 | false |
| request_pty | 向服务器请求伪终端 | false |
| curl_insecure | 允许 curl 连接无证书的 SSL 站点 | false |
| capture_stdout | 捕获命令的标准输出作为 Action 输出 | false |
| version | drone-ssh 二进制版本,未指定时使用最新版本 | |
---
@@ -120,6 +125,16 @@
---
## 📤 输出变量
本 Action 提供以下输出,可在后续步骤中使用:
| 输出 | 描述 |
| ------ | ----------------------------------------------------- |
| stdout | 执行命令的标准输出(需设置 `capture_stdout: true` |
---
## ⚡ 快速开始
只需简单配置,即可在工作流中执行远程 SSH 命令:
@@ -136,7 +151,7 @@ jobs:
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: linuxserver.io
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: whoami
@@ -148,7 +163,7 @@ jobs:
======CMD======
whoami
======END======
linuxserver.io
out: your_username
===============================================
✅ Successfully executed commands to all hosts.
===============================================
@@ -222,7 +237,7 @@ ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publ
在 Ubuntu 20.04+,你可能需要显式允许 `ssh-rsa` 算法。请在 OpenSSH 配置文件(`/etc/ssh/sshd_config``/etc/ssh/sshd_config.d/` 下的 drop-in 文件)中添加:
```bash
```text
CASignatureAlgorithms +ssh-rsa
```
@@ -366,6 +381,28 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
> _`env` 对象中的所有环境变量必须为字符串。传递整数或其他类型可能导致意外结果。_
### 捕获命令输出
你可以捕获远程命令的标准输出,并在后续步骤中使用:
```yaml
- name: 执行并捕获输出
id: ssh
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
capture_stdout: true
script: |
echo "Hello World"
hostname
- name: 使用捕获的输出
run: echo "SSH 输出为 ${{ steps.ssh.outputs.stdout }}"
```
---
## 🌐 代理与跳板机用法
@@ -380,7 +417,7 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
示例 `~/.ssh/config`
```bash
```text
Host Jumphost
HostName Jumphost
User ubuntu
+42 -5
View File
@@ -11,6 +11,7 @@
- [🔌 連線設定](#-連線設定)
- [🛠️ 指令設定](#-指令設定)
- [🌐 代理設定](#-代理設定)
- [📤 輸出變數](#-輸出變數)
- [⚡ 快速開始](#-快速開始)
- [🔑 SSH 金鑰設定與 OpenSSH 相容性](#-ssh-金鑰設定與-openssh-相容性)
- [設定 SSH 金鑰](#設定-ssh-金鑰)
@@ -26,6 +27,7 @@
- [多主機不同埠號](#多主機不同埠號)
- [多主機同步執行](#多主機同步執行)
- [傳遞環境變數到 shell 腳本](#傳遞環境變數到-shell-腳本)
- [擷取指令輸出](#擷取指令輸出)
- [🌐 代理與跳板機用法](#-代理與跳板機用法)
- [🛡️ 安全最佳實踐](#-安全最佳實踐)
- [保護你的私鑰](#保護你的私鑰)
@@ -43,11 +45,13 @@
**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 場景。
![ssh workflow](./images/ssh-workflow.png)
![ssh workflow](./images/ssh-agent.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)
[![Trivy Security Scan](https://github.com/appleboy/ssh-action/actions/workflows/trivy-scan.yml/badge.svg)](https://github.com/appleboy/ssh-action/actions/workflows/trivy-scan.yml)
**簡報:** [SSH for GitHub Actions](https://speakerdeck.com/appleboy/ssh-for-github-actions)
---
## 🧩 核心概念與輸入參數
@@ -93,6 +97,7 @@
| debug | 啟用除錯模式 | false |
| request_pty | 向伺服器請求偽終端 | false |
| curl_insecure | 允許 curl 連線無憑證的 SSL 網站 | false |
| capture_stdout | 擷取指令的標準輸出作為 Action 輸出 | false |
| version | drone-ssh 執行檔版本,未指定時使用最新版本 | |
---
@@ -120,6 +125,16 @@
---
## 📤 輸出變數
本 Action 提供以下輸出,可在後續步驟中使用:
| 輸出 | 說明 |
| ------ | ----------------------------------------------------- |
| stdout | 執行指令的標準輸出(需設定 `capture_stdout: true` |
---
## ⚡ 快速開始
只需簡單設定,即可在工作流程中執行遠端 SSH 指令:
@@ -136,7 +151,7 @@ jobs:
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: linuxserver.io
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
port: ${{ secrets.PORT }}
script: whoami
@@ -148,7 +163,7 @@ jobs:
======CMD======
whoami
======END======
linuxserver.io
out: your_username
===============================================
✅ Successfully executed commands to all hosts.
===============================================
@@ -222,7 +237,7 @@ ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publ
在 Ubuntu 20.04+,你可能需明確允許 `ssh-rsa` 演算法。請於 OpenSSH 設定檔(`/etc/ssh/sshd_config``/etc/ssh/sshd_config.d/` 下的 drop-in 檔案)加入:
```bash
```text
CASignatureAlgorithms +ssh-rsa
```
@@ -366,6 +381,28 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
> _`env` 物件中的所有環境變數必須為字串。傳遞整數或其他型別可能導致非預期結果。_
### 擷取指令輸出
你可以擷取遠端指令的標準輸出,並在後續步驟中使用:
```yaml
- name: 執行並擷取輸出
id: ssh
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
capture_stdout: true
script: |
echo "Hello World"
hostname
- name: 使用擷取的輸出
run: echo "SSH 輸出為 ${{ steps.ssh.outputs.stdout }}"
```
---
## 🌐 代理與跳板機用法
@@ -380,7 +417,7 @@ ssh-keygen -t ed25519 -a 200 -C "your_email@example.com"
範例 `~/.ssh/config`
```bash
```text
Host Jumphost
HostName Jumphost
User ubuntu
+41 -6
View File
@@ -6,7 +6,7 @@ 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}"
DRONE_SSH_VERSION="${DRONE_SSH_VERSION:-1.8.4}"
# Error codes
readonly ERR_UNKNOWN_PLATFORM=2
@@ -26,6 +26,8 @@ function detect_client_info() {
case "${CLIENT_PLATFORM}" in
darwin | linux | windows) ;;
# Git Bash / MSYS2 / Cygwin on Windows runners report e.g. MINGW64_NT-10.0
mingw* | msys* | cygwin*) CLIENT_PLATFORM="windows" ;;
*) log_error "Unknown or unsupported platform: ${CLIENT_PLATFORM}. Supported platforms are Linux, Darwin, and Windows." "${ERR_UNKNOWN_PLATFORM}" ;;
esac
@@ -39,6 +41,10 @@ function detect_client_info() {
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}"
# Windows release assets are published with an .exe suffix
if [[ "${CLIENT_PLATFORM}" == "windows" ]]; then
CLIENT_BINARY="${CLIENT_BINARY}.exe"
fi
TARGET="${GITHUB_ACTION_PATH}/${CLIENT_BINARY}"
# Check if binary already exists and is executable (caching)
@@ -62,6 +68,37 @@ else
log_error "Downloaded file is missing or empty: ${TARGET}" "${ERR_INVALID_BINARY}"
fi
# Verify checksum; container jobs may lack shasum (Perl) or sha256sum, so
# detect an available tool and skip verification with a warning if none exists
SHA256_CMD=""
if command -v shasum >/dev/null 2>&1; then
SHA256_CMD="shasum -a 256"
elif command -v sha256sum >/dev/null 2>&1; then
SHA256_CMD="sha256sum"
else
echo "Warning: neither shasum nor sha256sum is available, skipping checksum verification" >&2
fi
if [[ -n "${SHA256_CMD}" ]]; then
CHECKSUMS_FILE="${GITHUB_ACTION_PATH}/checksums.txt"
if ! curl -fsSL --retry 5 --keepalive-time 2 --location ${INSECURE_OPTION} \
"${DOWNLOAD_URL_PREFIX}/checksums.txt" -o "${CHECKSUMS_FILE}"; then
log_error "Failed to download checksums.txt from ${DOWNLOAD_URL_PREFIX}." "${ERR_DOWNLOAD_FAILED}"
fi
EXPECTED_CHECKSUM=$(awk -v bin="${CLIENT_BINARY}" '$2 == bin {print $1}' "${CHECKSUMS_FILE}")
if [[ -z "${EXPECTED_CHECKSUM}" ]]; then
log_error "No checksum entry found for ${CLIENT_BINARY} in checksums.txt." "${ERR_INVALID_BINARY}"
fi
ACTUAL_CHECKSUM=$(${SHA256_CMD} "${TARGET}" | awk '{print $1}')
if [[ "${ACTUAL_CHECKSUM}" != "${EXPECTED_CHECKSUM}" ]]; then
log_error "Checksum verification failed for ${CLIENT_BINARY}: expected ${EXPECTED_CHECKSUM}, got ${ACTUAL_CHECKSUM}." "${ERR_INVALID_BINARY}"
fi
echo "Checksum verification passed for ${CLIENT_BINARY}"
rm -f "${CHECKSUMS_FILE}"
fi
chmod +x "${TARGET}"
fi
@@ -71,11 +108,9 @@ if ! "${TARGET}" --version; then
fi
echo "======================================="
if [[ "${INPUT_CAPTURE_STDOUT}" == 'true' ]]; then
{
echo 'stdout<<EOF'
"${TARGET}" "$@" | tee -a "${GITHUB_OUTPUT}"
echo 'EOF'
} >>"${GITHUB_OUTPUT}"
echo 'stdout<<EOF' >> "${GITHUB_OUTPUT}"
"${TARGET}" "$@" | tee -a "${GITHUB_OUTPUT}"
echo 'EOF' >> "${GITHUB_OUTPUT}"
else
"${TARGET}" "$@"
fi
Binary file not shown.

After

Width:  |  Height:  |  Size: 652 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB